From caea747f1e346777826597d33d82bbd879626af5 Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Sun, 20 Jul 2025 15:24:44 +0300 Subject: [PATCH] BVH draw cache working --- src/DrawComponents/DrawComponent.cpp | 11 +- src/DrawComponents/DrawComponent.hpp | 3 +- src/DrawData.cpp | 12 +++ src/DrawData.hpp | 1 + src/DrawingProgram/DrawingProgram.cpp | 78 ++++++++++---- src/DrawingProgram/DrawingProgram.hpp | 4 +- src/DrawingProgram/DrawingProgramCache.cpp | 112 ++++++++++++++++++++- src/DrawingProgram/DrawingProgramCache.hpp | 18 +++- src/DrawingProgram/ScreenshotTool.cpp | 2 + src/MainProgram.hpp | 1 + src/Toolbar.cpp | 9 ++ src/World.cpp | 11 +- 12 files changed, 223 insertions(+), 39 deletions(-) diff --git a/src/DrawComponents/DrawComponent.cpp b/src/DrawComponents/DrawComponent.cpp index b935b18..4d14203 100644 --- a/src/DrawComponents/DrawComponent.cpp +++ b/src/DrawComponents/DrawComponent.cpp @@ -127,6 +127,12 @@ void DrawComponent::final_update(DrawingProgram& drawP) { updateDraw = true; } +void DrawComponent::final_update_dont_invalidate_cache(DrawingProgram& drawP) { + initialize_draw_data(drawP); + finalize_update(drawP, false); + updateDraw = true; +} + void DrawComponent::client_send_place(DrawingProgram& drawP, uint64_t placement) { drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_PLACE_COMPONENT, placement, get_type(), this->coords, *this); } @@ -151,8 +157,9 @@ void DrawComponent::client_send_transform_final(DrawingProgram& drawP, ServerCli drawP.world.con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_TRANSFORM_COMPONENT, false, id, this->coords); } -void DrawComponent::finalize_update(DrawingProgram& drawP) { - drawP.compCache.preupdate_component(this); +void DrawComponent::finalize_update(DrawingProgram& drawP, bool invalidateCache) { + if(invalidateCache) + drawP.compCache.preupdate_component(this); create_collider(drawP.colliderAllocated); updateDraw = true; } diff --git a/src/DrawComponents/DrawComponent.hpp b/src/DrawComponents/DrawComponent.hpp index 4bc6529..9799b9f 100644 --- a/src/DrawComponents/DrawComponent.hpp +++ b/src/DrawComponents/DrawComponent.hpp @@ -100,9 +100,10 @@ class DrawComponent { virtual void draw(SkCanvas* canvas, const DrawData& drawData) = 0; void temp_update(DrawingProgram& drawP); void final_update(DrawingProgram& drawP); + void final_update_dont_invalidate_cache(DrawingProgram& drawP); void transform_temp_update(DrawingProgram& drawP); virtual void initialize_draw_data(DrawingProgram& drawP) = 0; - virtual void finalize_update(DrawingProgram& drawP); + virtual void finalize_update(DrawingProgram& drawP, bool invalidateCache = true); virtual void update(DrawingProgram& drawP) = 0; virtual bool collides_within_coords(const SCollision::ColliderCollection& checkAgainst, bool colliderAllocated) = 0; bool collides_with_world_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection& checkAgainstWorld, bool colliderAllocated); diff --git a/src/DrawData.cpp b/src/DrawData.cpp index e7fdb09..414da87 100644 --- a/src/DrawData.cpp +++ b/src/DrawData.cpp @@ -1 +1,13 @@ #include "DrawData.hpp" +#include "DrawComponents/DrawComponent.hpp" + +void DrawData::refresh_draw_optimizing_values() { + clampDrawBetween = true; + clampDrawMinimum = cam.c.inverseScale >> DRAWCOMP_MIN_SHIFT_BEFORE_DISAPPEAR; + mipMapLevelOne = cam.c.inverseScale >> DRAWCOMP_MIPMAP_LEVEL_ONE; + mipMapLevelTwo = cam.c.inverseScale >> DRAWCOMP_MIPMAP_LEVEL_TWO; + mipMapLevelThree = cam.c.inverseScale >> DRAWCOMP_MIPMAP_LEVEL_THREE; + mipMapLevelFour = cam.c.inverseScale >> DRAWCOMP_MIPMAP_LEVEL_FOUR; + mipMapLevelFive = cam.c.inverseScale >> DRAWCOMP_MIPMAP_LEVEL_FOUR; + clampDrawMaximum = cam.c.inverseScale << DRAWCOMP_MAX_SHIFT_BEFORE_DISAPPEAR; +} diff --git a/src/DrawData.hpp b/src/DrawData.hpp index 98d3063..a42d553 100644 --- a/src/DrawData.hpp +++ b/src/DrawData.hpp @@ -21,4 +21,5 @@ struct DrawData { WorldScalar mipMapLevelThree; WorldScalar mipMapLevelFour; WorldScalar mipMapLevelFive; + void refresh_draw_optimizing_values(); }; diff --git a/src/DrawingProgram/DrawingProgram.cpp b/src/DrawingProgram/DrawingProgram.cpp index 2130f94..67d3c4a 100644 --- a/src/DrawingProgram/DrawingProgram.cpp +++ b/src/DrawingProgram/DrawingProgram.cpp @@ -28,11 +28,17 @@ #include #endif -template void parallel_loop_vector_any_type() { +template void parallel_loop_container(ContainerType& c, std::function func) { +#ifdef __EMSCRIPTEN__ + std::for_each(c.begin(), c.end(), func); +#else + std::for_each(std::execution::par_unseq, c.begin(), c.end(), func); +#endif } DrawingProgram::DrawingProgram(World& initWorld): world(initWorld), + compCache(*this), components([&](){ return world.get_new_id(); }), brushTool(*this), eraserTool(*this), @@ -134,11 +140,7 @@ void DrawingProgram::allocate_collider_memory() { } void DrawingProgram::parallel_loop_all_components(std::function, ServerClientID>::ObjectInfo>&)> func) { -#ifdef __EMSCRIPTEN__ - std::for_each(components.client_list().begin(), components.client_list().end(), func); -#else - std::for_each(std::execution::par_unseq, components.client_list().begin(), components.client_list().end(), func); -#endif + parallel_loop_container(components.client_list(), func); } void DrawingProgram::check_all_collisions_base(const SCollision::ColliderCollection& checkAgainstWorld, const SCollision::ColliderCollection& checkAgainstCam) { @@ -249,7 +251,6 @@ void DrawingProgram::tool_options_gui() { default: break; } - t.gui.checkbox_field("Disable Draw Cache", "Disable Cache", &disableCache); } } @@ -358,7 +359,7 @@ void DrawingProgram::update() { c->obj->update(*this); } - compCache.update(components.client_list()); + compCache.update(); } void DrawingProgram::drag_drop_update() { @@ -446,7 +447,7 @@ void DrawingProgram::initialize_draw_data(cereal::PortableBinaryInputArchive& a) auto newComp = DrawComponent::allocate_comp_type(t); a(newComp->coords, *newComp); components.init_emplace_back(id, newComp); - newComp->final_update(*this); + newComp->final_update_dont_invalidate_cache(*this); } } @@ -550,24 +551,23 @@ void DrawingProgram::draw(SkCanvas* canvas, const DrawData& drawData) { c->obj->draw(canvas, drawData); } else { - if(disableCache) { + if(world.main.drawProgCache.disableDrawCache) { parallel_loop_all_components([&](auto& c) { c->obj->calculate_draw_transform(drawData); }); + for(auto& c : components.client_list()) + c->obj->draw(canvas, drawData); } else { - for(auto& c : components.client_list()) - c->obj->drawSetupData.shouldDraw = false; - compCache.traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](DrawingProgramCache::BVHNode* node, const std::vector& comps) { - for(auto& c : comps) - c->obj->calculate_draw_transform(drawData); - return true; - }); + SkCanvas* drawProgCacheCanvas = world.main.drawProgCache.surface->getCanvas(); + drawProgCacheCanvas->clear(SkColor4f{0.0f, 0.0f, 0.0f, 0.0f}); + compCache.refresh_all_draw_cache(drawProgCacheCanvas, drawData); + draw_components_to_canvas(drawProgCacheCanvas, drawData, false); + canvas->drawImage(world.main.drawProgCache.surface->makeTemporaryImage(), 0, 0); } - for(auto& c : components.client_list()) { - c->obj->draw(canvas, drawData); + + for(auto& c : components.client_list()) c->obj->updateDraw = false; - } } switch(controls.selectedTool) { @@ -603,6 +603,44 @@ void DrawingProgram::draw(SkCanvas* canvas, const DrawData& drawData) { } } +void DrawingProgram::draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, bool dontUseCache) { + for(auto& c : components.client_list()) + c->obj->drawSetupData.shouldDraw = false; + + std::vector cachedNodesToDraw; + + compCache.traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](DrawingProgramCache::BVHNode* node, const std::vector& comps) { + if(!dontUseCache && node && node->drawCache && node->coords.inverseScale <= drawData.cam.c.inverseScale) { + cachedNodesToDraw.emplace_back(node); + return false; + } + for(auto& c : comps) + c->obj->calculate_draw_transform(drawData); + return true; + }); + + for(uint64_t i = 0; i < components.client_list().size(); i++) { + auto& c = components.client_list()[i]; + c->obj->draw(canvas, drawData); + for(DrawingProgramCache::BVHNode* bvhNode : cachedNodesToDraw) { + auto& drawCache = bvhNode->drawCache.value(); + if(drawCache.lastDrawnComponentPlacement == i) { + canvas->save(); + bvhNode->coords.transform_sk_canvas(canvas, drawData); + drawCache.lastRenderTime = std::chrono::steady_clock::now(); + + SkPaint p; + p.setBlendMode(SkBlendMode::kSrc); + + // Note, no mipmaps here. You might need to generate mipmaps in the future (withDefaultMipmaps) + canvas->drawImage(drawCache.surface->makeTemporaryImage(), 0, 0, {SkFilterMode::kLinear, SkMipmapMode::kLinear}, &p); + + canvas->restore(); + } + } + } +} + Vector4f* DrawingProgram::get_foreground_color_ptr() { return &controls.foregroundColor; } diff --git a/src/DrawingProgram/DrawingProgram.hpp b/src/DrawingProgram/DrawingProgram.hpp index 07908fa..2579e50 100644 --- a/src/DrawingProgram/DrawingProgram.hpp +++ b/src/DrawingProgram/DrawingProgram.hpp @@ -55,6 +55,7 @@ class DrawingProgram { CollabListType components; + Vector4f* get_foreground_color_ptr(); private: std::atomic addFileInNextFrame = false; @@ -79,7 +80,7 @@ class DrawingProgram { void add_undo_place_component(uint64_t placement, const std::shared_ptr& comp); void add_undo_place_components(uint64_t placement, const std::vector>& comps); - bool disableCache = true; + void draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, bool dontUseCache); BrushTool brushTool; EraserTool eraserTool; @@ -135,4 +136,5 @@ class DrawingProgram { friend class ScreenshotTool; friend class EditTool; friend class ImageTool; + friend class DrawingProgramCache; }; diff --git a/src/DrawingProgram/DrawingProgramCache.cpp b/src/DrawingProgram/DrawingProgramCache.cpp index b159793..d7bb51a 100644 --- a/src/DrawingProgram/DrawingProgramCache.cpp +++ b/src/DrawingProgram/DrawingProgramCache.cpp @@ -1,8 +1,26 @@ #include "DrawingProgramCache.hpp" +#include "DrawingProgram.hpp" +#include "../World.hpp" +#include "../MainProgram.hpp" +#include + +#ifdef USE_SKIA_BACKEND_GRAPHITE + #include +#elif USE_SKIA_BACKEND_GANESH + #include + #include +#endif + +#define MAXIMUM_DRAW_CACHE_SURFACES 32 + +DrawingProgramCache::DrawingProgramCache(DrawingProgram& initDrawP): + drawP(initDrawP) +{} void DrawingProgramCache::build(std::vector components) { bvhRoot = BVHNode(); unsortedComponents.clear(); + nodesWithCachedSurfaces.clear(); std::erase_if(components, [&](auto& c) { if(!c->obj->worldAABB || c->obj->updateDraw) { unsortedComponents.emplace_back(c); @@ -13,9 +31,9 @@ void DrawingProgramCache::build(std::vector compo build_bvh_node(bvhRoot, components); } -void DrawingProgramCache::update(const std::vector& components) { - if(unsortedComponents.size() >= 1) - build(components); +void DrawingProgramCache::update() { + if(unsortedComponents.size() >= 750) + build(drawP.components.client_list()); } void DrawingProgramCache::add_component(const CollabListType::ObjectInfoPtr& c) { @@ -94,7 +112,9 @@ void DrawingProgramCache::build_bvh_node(BVHNode& node, std::vectorobj->worldAABB.value()); - if(components.size() < 50) { + build_bvh_node_coords_and_resolution(node); + + if(components.size() < 100) { node.components = components; return; } @@ -123,6 +143,31 @@ void DrawingProgramCache::build_bvh_node(BVHNode& node, std::vector cacheBoundDim.y()) { + node.resolution.x() = MAX_DIMENSION_RESOLUTION; + node.resolution.y() = MAX_DIMENSION_RESOLUTION * static_cast(cacheBoundDim.y() / cacheBoundDim.x()); + } + else { + node.resolution.y() = MAX_DIMENSION_RESOLUTION; + node.resolution.x() = MAX_DIMENSION_RESOLUTION * static_cast(cacheBoundDim.x() / cacheBoundDim.y()); + } + node.coords.rotation = 0.0; + node.coords.pos = node.bounds.min; + // Not necessary, but we choose the number that had less operations done on it for more accuracy + if(node.resolution.x() > node.resolution.y()) + node.coords.inverseScale = cacheBoundDim.x().divide_double(node.resolution.x()); + else + node.coords.inverseScale = cacheBoundDim.y().divide_double(node.resolution.y()); +} + void DrawingProgramCache::invalidate_cache_before_pos(uint64_t placementToInvalidateAt) { // We could use worldAABB of component to invalidate specific areas. However, that comes with some issues: // - The AABB couldve been changed from the last frame, so the current worldAABB doesnt actually represent what we should update @@ -137,3 +182,62 @@ void DrawingProgramCache::invalidate_cache_before_pos(uint64_t placementToInvali return false; }); } + +void DrawingProgramCache::refresh_all_draw_cache(SkCanvas* canvas, 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, [&](DrawingProgramCache::BVHNode* node, const std::vector& comps) { + if(node && node->coords.inverseScale <= drawData.cam.c.inverseScale) { + if(!node->drawCache) + nodeFlatList.emplace_front(node); + return false; + } + return true; + }); + if(!nodeFlatList.empty()) + refresh_draw_cache(*nodeFlatList.front(), drawData); +} + +void DrawingProgramCache::refresh_draw_cache(BVHNode& bvhNode, const DrawData& drawData) { + if(bvhNode.children.empty() && bvhNode.components.empty()) + return; + + bvhNode.drawCache = std::nullopt; + BVHNode::DrawCacheData drawCache; + + 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 + + if(!drawCache.surface) + throw std::runtime_error("[DrawingProgramCache::refresh_draw_cache] Could not make cache surface"); + + SkCanvas* cacheCanvas = drawCache.surface->getCanvas(); + + cacheCanvas->clear(SkColor4f{0, 0, 0, 0}); + + DrawData cacheDrawData = drawData; + cacheDrawData.cam.c = bvhNode.coords; + cacheDrawData.cam.changed = nullptr; + cacheDrawData.cam.set_viewing_area(bvhNode.resolution.cast()); + cacheDrawData.refresh_draw_optimizing_values(); + drawP.draw_components_to_canvas(cacheCanvas, cacheDrawData, false); + + drawCache.lastDrawnComponentPlacement = drawP.components.client_list().size() - 1; + drawCache.lastRenderTime = std::chrono::steady_clock::now(); + + bvhNode.drawCache = drawCache; // Set the drawCache after rendering is done, so that the draw function doesnt assume we have this node cached + + while(nodesWithCachedSurfaces.size() >= MAXIMUM_DRAW_CACHE_SURFACES) { + BVHNode* leastUsedNode = *nodesWithCachedSurfaces.begin(); + for(BVHNode* n : nodesWithCachedSurfaces) { + if(n->drawCache.value().lastRenderTime < leastUsedNode->drawCache.value().lastRenderTime) + leastUsedNode = n; + } + nodesWithCachedSurfaces.erase(leastUsedNode); + } + + nodesWithCachedSurfaces.emplace(&bvhNode); +} diff --git a/src/DrawingProgram/DrawingProgramCache.hpp b/src/DrawingProgram/DrawingProgramCache.hpp index e93b183..8d9c331 100644 --- a/src/DrawingProgram/DrawingProgramCache.hpp +++ b/src/DrawingProgram/DrawingProgramCache.hpp @@ -3,26 +3,37 @@ #include "../CollabList.hpp" #include "../DrawComponents/DrawComponent.hpp" #include +#include + +class DrawingProgram; class DrawingProgramCache { public: typedef CollabList, ServerClientID> CollabListType; - void update(const std::vector& components); + DrawingProgramCache(DrawingProgram& initDrawP); + + void update(); void erase_component(const CollabListType::ObjectInfoPtr& c); void add_component(const CollabListType::ObjectInfoPtr& c); + void refresh_all_draw_cache(SkCanvas* canvas, const DrawData& drawData); struct BVHNode { SCollision::AABB bounds; std::vector components; std::vector children; + + CoordSpaceHelper coords; + Vector2i resolution; struct DrawCacheData { uint64_t lastDrawnComponentPlacement; sk_sp surface; + std::chrono::steady_clock::time_point lastRenderTime; }; + std::optional drawCache; }; - std::vector nodesWithCachedSurfaces; + std::unordered_set nodesWithCachedSurfaces; void traverse_bvh_run_function(const SCollision::AABB& aabb, std::function& components)> f); @@ -31,6 +42,8 @@ class DrawingProgramCache { void invalidate_cache_before_pos(uint64_t placementToInvalidateAt); private: + void build_bvh_node_coords_and_resolution(BVHNode& node); + void refresh_draw_cache(BVHNode& bvhNode, const DrawData& drawData); void build(std::vector components); @@ -40,4 +53,5 @@ class DrawingProgramCache { void build_bvh_node(BVHNode& node, std::vector components); BVHNode bvhRoot; std::vector unsortedComponents; + DrawingProgram& drawP; }; diff --git a/src/DrawingProgram/ScreenshotTool.cpp b/src/DrawingProgram/ScreenshotTool.cpp index 2a7890b..9ada135 100644 --- a/src/DrawingProgram/ScreenshotTool.cpp +++ b/src/DrawingProgram/ScreenshotTool.cpp @@ -181,6 +181,8 @@ void ScreenshotTool::take_screenshot_area_hw(const sk_sp& surface, Sk WorldScalar newInverseScale = (vectorZoom.x() + vectorZoom.y()) * WorldScalar(0.5); drawP.world.drawData.cam.set_based_on_properties(drawP.world, topLeft, newInverseScale, controls.coords.rotation); + drawP.world.drawData.cam.set_viewing_area(sectionImageSize.cast()); + drawP.world.drawData.refresh_draw_optimizing_values(); GridManager::GridType oldGridType = drawP.world.main.grid.gridType; drawP.world.main.takingScreenshot = true; diff --git a/src/MainProgram.hpp b/src/MainProgram.hpp index 292472f..da35831 100644 --- a/src/MainProgram.hpp +++ b/src/MainProgram.hpp @@ -35,6 +35,7 @@ class MainProgram { sk_sp surface; std::shared_ptr firstCompUpdate = nullptr; SkCanvas* canvas; + bool disableDrawCache = false; } drawProgCache; struct Window { diff --git a/src/Toolbar.cpp b/src/Toolbar.cpp index 01166cf..596a554 100644 --- a/src/Toolbar.cpp +++ b/src/Toolbar.cpp @@ -221,6 +221,7 @@ nlohmann::json Toolbar::get_config_json() { toRet["themeInUse"] = themeData.themeCurrentlyLoaded; toRet["velocityAffectsBrushWidth"] = velocityAffectsBrushWidth; toRet["jumpTransitionEasing"] = jumpTransitionEasing; + toRet["disableDrawCache"] = main.drawProgCache.disableDrawCache; json tablet; tablet["pressureAffectsBrushWidth"] = tabletOptions.pressureAffectsBrushWidth; @@ -271,6 +272,7 @@ void Toolbar::set_config_json(const nlohmann::json& j) { j.at("themeInUse").get_to(themeData.themeCurrentlyLoaded); j.at("velocityAffectsBrushWidth").get_to(velocityAffectsBrushWidth); j.at("jumpTransitionEasing").get_to(jumpTransitionEasing); + j.at("disableDrawCache").get_to(main.drawProgCache.disableDrawCache); j.at("tablet").at("pressureAffectsBrushWidth").get_to(tabletOptions.pressureAffectsBrushWidth); j.at("tablet").at("smoothingSamplingTime").get_to(tabletOptions.smoothingSamplingTime); @@ -1156,6 +1158,7 @@ void Toolbar::options_menu() { }) { switch(generalSettingsOptions) { case GSETTINGS_GENERAL: { + gui.push_id("general settings"); gui.input_text_field("display name input", "Display Name", &main.displayName); main.update_display_names(); gui.text_label("Note: Changes in display name dont affect canvases that are connected online"); @@ -1166,9 +1169,11 @@ void Toolbar::options_menu() { gui.slider_scalar_field("scroll zoom slider", "Scroll zoom speed", &scrollZoomSpeed, 0.0, 1.0, 3); gui.input_scalar_field("jump transition time", "Jump transition time", &jumpTransitionTime, 0.01f, 1000.0f, 2); gui.checkbox_field("changebrushwidthwithspeed", "Change brush size with mouse speed", &velocityAffectsBrushWidth); + gui.pop_id(); break; } case GSETTINGS_APPEARANCE: { + gui.push_id("appearance settings"); if(gui.radio_button_field("grid type no grid", "No Grid", (main.grid.gridType == GridManager::GRIDTYPE_NONE))) main.grid.gridType = GridManager::GRIDTYPE_NONE; if(gui.radio_button_field("grid type circle grid", "Circle Dot Grid", (main.grid.gridType == GridManager::GRIDTYPE_CIRCLEDOT))) main.grid.gridType = GridManager::GRIDTYPE_CIRCLEDOT; if(gui.radio_button_field("grid type square grid", "Square Dot Grid", (main.grid.gridType == GridManager::GRIDTYPE_SQUAREDOT))) main.grid.gridType = GridManager::GRIDTYPE_SQUAREDOT; @@ -1176,6 +1181,7 @@ void Toolbar::options_menu() { gui.input_scalar_field("GUI Scale", "GUI Scale", &guiScale, 0.5f, 3.0f, 1); gui.input_scalar_field("GUI Font Size", "GUI Font Size", &io->fontSize, 10, 30); gui.text_label("Note: Changing font size may break UI in some cases."); + gui.pop_id(); break; } case GSETTINGS_TABLET: { @@ -1303,6 +1309,7 @@ void Toolbar::options_menu() { break; } case GSETTINGS_DEBUG: { + gui.push_id("debug settings menu"); gui.checkbox_field("show performance metrics", "Show Metrics", &showPerformance); gui.input_scalar_fields("jump transition easing", "Jump Easing", &jumpTransitionEasing, 4, -10.0f, 10.0f, 2); #ifndef __EMSCRIPTEN__ @@ -1310,6 +1317,8 @@ void Toolbar::options_menu() { gui.text_label("Note: Requires restart to take effect. May cause bugs on Intel Graphics"); gui.input_scalar_field("fps cap slider", "FPS cap", &main.fpsLimit, 3.0f, 10000.0f); #endif + gui.checkbox_field("Disable Draw Cache", "Disable BVH Draw Cache", &main.drawProgCache.disableDrawCache); + gui.pop_id(); break; } } diff --git a/src/World.cpp b/src/World.cpp index e20c0dc..61f6ae3 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -263,6 +263,7 @@ void World::save_to_file(const std::filesystem::path& fileName) { void World::load_from_file(const std::filesystem::path& fileName, std::string_view buffer) { filePath = force_extension_on_path(fileName, FILE_EXTENSION); + if(buffer.empty()) { std::ifstream f(filePath, std::ios::in | std::ios::binary); std::string fileHeader; @@ -319,15 +320,7 @@ WorldScalar World::calculate_zoom_from_uniform_zoom(WorldScalar uniformZoom, Wor } void World::draw(SkCanvas* canvas) { - drawData.clampDrawBetween = true; - - drawData.clampDrawMinimum = drawData.cam.c.inverseScale >> DRAWCOMP_MIN_SHIFT_BEFORE_DISAPPEAR; - drawData.mipMapLevelOne = drawData.cam.c.inverseScale >> DRAWCOMP_MIPMAP_LEVEL_ONE; - drawData.mipMapLevelTwo = drawData.cam.c.inverseScale >> DRAWCOMP_MIPMAP_LEVEL_TWO; - drawData.mipMapLevelThree = drawData.cam.c.inverseScale >> DRAWCOMP_MIPMAP_LEVEL_THREE; - drawData.mipMapLevelFour = drawData.cam.c.inverseScale >> DRAWCOMP_MIPMAP_LEVEL_FOUR; - drawData.mipMapLevelFive = drawData.cam.c.inverseScale >> DRAWCOMP_MIPMAP_LEVEL_FOUR; - drawData.clampDrawMaximum = drawData.cam.c.inverseScale << DRAWCOMP_MAX_SHIFT_BEFORE_DISAPPEAR; + drawData.refresh_draw_optimizing_values(); drawProg.draw(canvas, drawData);