diff --git a/CMakeLists.txt b/CMakeLists.txt index 9314e89..c395a9c 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,7 @@ set(sources "src/main.cpp" "src/RichText/TextBox.cpp" "src/RichText/TextStyleModifier.cpp" "src/RichText/ParagraphStyleData.cpp" + "src/DrawingProgram/DrawingProgramCacheNew.cpp" "src/DrawingProgram/DrawingProgramCache.cpp" "src/DrawingProgram/DrawingProgram.cpp" "src/DrawingProgram/DrawingProgramSelection.cpp" diff --git a/src/DrawingProgram/DrawingProgramCacheNew.cpp b/src/DrawingProgram/DrawingProgramCacheNew.cpp new file mode 100644 index 0000000..4b45df3 --- /dev/null +++ b/src/DrawingProgram/DrawingProgramCacheNew.cpp @@ -0,0 +1,141 @@ +#include "DrawingProgramCacheNew.hpp" +#include "DrawingProgram.hpp" + +std::unordered_set> DrawingProgramCacheNew::nodesWithCachedSurfaces; + +DrawingProgramCacheNew::DrawingProgramCacheNew(DrawingProgram& initDrawP): + drawP(initDrawP) +{} + +void DrawingProgramCacheNew::add_component(const CanvasComponentContainer::ObjInfoSharedPtr& c) { + unsortedComponents.emplace_back(c); + invalidate_cache_at_aabb(c->obj->get_world_bounds()); +} + +void DrawingProgramCacheNew::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& c) { + auto it = objToBVHNode.find(c); + if(it != objToBVHNode.end()) + objToBVHNode.erase(it); + else + std::erase(unsortedComponents, c); + invalidate_cache_at_aabb(c->obj->get_world_bounds()); +} + +void DrawingProgramCacheNew::invalidate_cache_at_aabb(const SCollision::AABB& aabb) { + std::for_each(nodesWithCachedSurfaces.begin(), nodesWithCachedSurfaces.end(), [&](auto& n) { + if(n->drawCache.value().attachedCache == this && SCollision::collide(aabb, n->bounds)) { + auto& dCache = n->drawCache.value(); + if(dCache.invalidBounds.has_value()) { + auto& iBounds = dCache.invalidBounds.value(); + iBounds.include_aabb_in_bounds(aabb); + } + else + dCache.invalidBounds = aabb; + } + }); +} + +bool DrawingProgramCacheNew::should_rebuild() const { + return (unsortedComponents.size() >= MINIMUM_COMPONENTS_TO_START_REBUILD); +} + +void DrawingProgramCacheNew::build(const std::unordered_set& objsToExclude) { + internal_build(drawP.layerMan.get_flattened_component_list(), objsToExclude); +} + +void DrawingProgramCacheNew::internal_build(std::vector componentsToBuild, const std::unordered_set& objsToNotInclude) { + std::erase_if(componentsToBuild, [&objsToNotInclude](auto& c) { + return objsToNotInclude.contains(c); + }); + bvhRoot = std::make_shared(); + unsortedComponents.clear(); + clear_own_cached_surfaces(); + build_bvh_node(bvhRoot, componentsToBuild); +} + +void DrawingProgramCacheNew::clear_own_cached_surfaces() { + std::erase_if(nodesWithCachedSurfaces, [&](auto& n) { + if(n->drawCache.value().attachedCache == this) { + n->drawCache = std::nullopt; + return true; + } + return false; + }); +} + +void DrawingProgramCacheNew::preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& c) { + auto it = objToBVHNode.find(c); + if(it != objToBVHNode.end()) { + unsortedComponents.emplace_back(c); + std::erase(it->second->components, c); + } + invalidate_cache_at_aabb(c->obj->get_world_bounds()); +} + +void DrawingProgramCacheNew::build_bvh_node(const std::shared_ptr& bvhNode, const std::vector& components) { + if(components.empty()) + return; + + bvhNode->bounds = components.front()->obj->get_world_bounds(); + for(auto& c : components) + bvhNode->bounds.include_aabb_in_bounds(c->obj->get_world_bounds()); + + build_bvh_node_coords_and_resolution(*bvhNode); + + if(components.size() < MAXIMUM_COMPONENTS_IN_SINGLE_NODE) { + bvhNode->components = components; + for(auto& c : bvhNode->components) + objToBVHNode.emplace(c, bvhNode); + return; + } + + WorldVec boundsCenter = bvhNode->bounds.center(); + + std::array, 4> parts; + + for(auto& c : components) { + const auto& cAABB = c->obj->get_world_bounds(); + if(cAABB.min.x() < boundsCenter.x() && cAABB.max.x() < boundsCenter.x() && cAABB.min.y() < boundsCenter.y() && cAABB.max.y() < boundsCenter.y()) + parts[0].emplace_back(c); + else if(cAABB.min.x() > boundsCenter.x() && cAABB.max.x() > boundsCenter.x() && cAABB.min.y() < boundsCenter.y() && cAABB.max.y() < boundsCenter.y()) + parts[1].emplace_back(c); + else if(cAABB.min.x() < boundsCenter.x() && cAABB.max.x() < boundsCenter.x() && cAABB.min.y() > boundsCenter.y() && cAABB.max.y() > boundsCenter.y()) + parts[2].emplace_back(c); + else if(cAABB.min.x() > boundsCenter.x() && cAABB.max.x() > boundsCenter.x() && cAABB.min.y() > boundsCenter.y() && cAABB.max.y() > boundsCenter.y()) + parts[3].emplace_back(c); + else { + bvhNode->components.emplace_back(c); + objToBVHNode.emplace(c, bvhNode); + } + } + + for(auto& p : parts) { + if(!p.empty()) + build_bvh_node(bvhNode->children.emplace_back(std::make_shared()), p); + } +} + +void DrawingProgramCacheNew::build_bvh_node_coords_and_resolution(BVHNode& node) { + // These are part of the draw cache data, but we're putting it out here, because: + // - We dont have to recalculate it every time we refresh the cache + // - Some of the data here is needed to determine whether to generate the cache in the first place + + constexpr int MAX_DIMENSION_RESOLUTION = 2048; + + WorldVec cacheBoundDim = node.bounds.dim(); + if(cacheBoundDim.x() > 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()); +} diff --git a/src/DrawingProgram/DrawingProgramCacheNew.hpp b/src/DrawingProgram/DrawingProgramCacheNew.hpp new file mode 100644 index 0000000..2d4b7d5 --- /dev/null +++ b/src/DrawingProgram/DrawingProgramCacheNew.hpp @@ -0,0 +1,46 @@ +#pragma once +#include "../CanvasComponents/CanvasComponentContainer.hpp" +#include + +class DrawingProgramCacheNew { + public: + static constexpr size_t MINIMUM_COMPONENTS_TO_START_REBUILD = 1000; + static constexpr size_t MAXIMUM_COMPONENTS_IN_SINGLE_NODE = 50; + static constexpr size_t MAXIMUM_DRAW_CACHE_SURFACES = 40; + + DrawingProgramCacheNew(DrawingProgram& initDrawP); + void add_component(const CanvasComponentContainer::ObjInfoSharedPtr& c); + void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& c); + void clear_own_cached_surfaces(); + void preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& c); + void build(const std::unordered_set& objsToExclude); + bool should_rebuild() const; + private: + struct BVHNode { + SCollision::AABB bounds; + std::vector components; + std::vector> children; + + CoordSpaceHelper coords; + Vector2i resolution; + struct DrawCacheData { + sk_sp surface; + std::chrono::steady_clock::time_point lastRenderTime; + DrawingProgramCacheNew* attachedCache; + std::optional> invalidBounds; + }; + + std::optional drawCache; + }; + static std::unordered_set> nodesWithCachedSurfaces; + + void internal_build(std::vector componentsToBuild, const std::unordered_set& objsToNotInclude); + void invalidate_cache_at_aabb(const SCollision::AABB& aabb); + void build_bvh_node(const std::shared_ptr& bvhNode, const std::vector& components); + void build_bvh_node_coords_and_resolution(BVHNode& node); + + std::unordered_map> objToBVHNode; + std::shared_ptr bvhRoot; + std::vector unsortedComponents; + DrawingProgram& drawP; +}; diff --git a/src/DrawingProgram/Layers/DrawingProgramLayer.cpp b/src/DrawingProgram/Layers/DrawingProgramLayer.cpp index 716523d..5e5c912 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayer.cpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayer.cpp @@ -3,20 +3,24 @@ #include "../DrawingProgram.hpp" #include -void DrawingProgramLayer::draw(SkCanvas* canvas, const DrawData& drawData) { +void DrawingProgramLayer::draw(SkCanvas* canvas, const DrawData& drawData) const { for(auto& p : components->get_data()) p->obj->draw(canvas, drawData); } -void DrawingProgramLayer::set_component_list_callbacks(DrawingProgramLayerManager& layerMan) { +void DrawingProgramLayer::set_component_list_callbacks(DrawingProgramLayerManager& layerMan) const { components->set_insert_callback([&](const CanvasComponentContainer::ObjInfoSharedPtr& c) { c->obj->set_owner_obj_info(c); c->obj->commit_update(layerMan.drawP); // Run commit update on insert so that world bounds are calculated }); } -void DrawingProgramLayer::commit_update_dont_invalidate_cache(DrawingProgramLayerManager& layerMan) { +void DrawingProgramLayer::commit_update_dont_invalidate_cache(DrawingProgramLayerManager& layerMan) const { parallel_loop_container(components->get_data(), [&layerMan](auto& comp) { comp->obj->commit_update_dont_invalidate_cache(layerMan.drawP); }); } + +void DrawingProgramLayer::get_flattened_component_list(std::vector& objList) const { + objList.insert(objList.begin(), components->get_data().begin(), components->get_data().end()); +} diff --git a/src/DrawingProgram/Layers/DrawingProgramLayer.hpp b/src/DrawingProgram/Layers/DrawingProgramLayer.hpp index 4694d1b..1886e3f 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayer.hpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayer.hpp @@ -7,9 +7,10 @@ class DrawingProgramLayerManager; class DrawingProgramLayer { public: - void draw(SkCanvas* canvas, const DrawData& drawData); - void set_component_list_callbacks(DrawingProgramLayerManager& layerMan); - void commit_update_dont_invalidate_cache(DrawingProgramLayerManager& layerMan); + void draw(SkCanvas* canvas, const DrawData& drawData) const; + void set_component_list_callbacks(DrawingProgramLayerManager& layerMan) const; + void commit_update_dont_invalidate_cache(DrawingProgramLayerManager& layerMan) const; + void get_flattened_component_list(std::vector& objList) const; CanvasComponentContainer::NetListOwnerPtr components; }; diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerFolder.cpp b/src/DrawingProgram/Layers/DrawingProgramLayerFolder.cpp index 08f4827..82c8b04 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayerFolder.cpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayerFolder.cpp @@ -3,12 +3,12 @@ #include "DrawingProgramLayer.hpp" #include -void DrawingProgramLayerFolder::draw(SkCanvas* canvas, const DrawData& drawData) { +void DrawingProgramLayerFolder::draw(SkCanvas* canvas, const DrawData& drawData) const { for(auto& p : folderList->get_data() | std::views::reverse) p->obj->draw(canvas, drawData); } -void DrawingProgramLayerFolder::set_component_list_callbacks(DrawingProgramLayerManager& layerMan) { +void DrawingProgramLayerFolder::set_component_list_callbacks(DrawingProgramLayerManager& layerMan) const { folderList->set_insert_callback([&](auto& c) { c->obj->set_component_list_callbacks(layerMan); }); @@ -16,7 +16,12 @@ void DrawingProgramLayerFolder::set_component_list_callbacks(DrawingProgramLayer listItem->obj->set_component_list_callbacks(layerMan); } -void DrawingProgramLayerFolder::commit_update_dont_invalidate_cache(DrawingProgramLayerManager& layerMan) { +void DrawingProgramLayerFolder::commit_update_dont_invalidate_cache(DrawingProgramLayerManager& layerMan) const { for(auto& listItem : folderList->get_data()) listItem->obj->commit_update_dont_invalidate_cache(layerMan); } + +void DrawingProgramLayerFolder::get_flattened_component_list(std::vector& objList) const { + for(auto& listItem : folderList->get_data()) + listItem->obj->get_flattened_component_list(objList); +} diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerFolder.hpp b/src/DrawingProgram/Layers/DrawingProgramLayerFolder.hpp index d008455..5ed7907 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayerFolder.hpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayerFolder.hpp @@ -2,15 +2,17 @@ #include #include #include "../../DrawData.hpp" +#include "../../CanvasComponents/CanvasComponentContainer.hpp" class DrawingProgramLayerListItem; class DrawingProgramLayerManager; class DrawingProgramLayerFolder { public: - void draw(SkCanvas* canvas, const DrawData& drawData); - void set_component_list_callbacks(DrawingProgramLayerManager& layerMan); - void commit_update_dont_invalidate_cache(DrawingProgramLayerManager& layerMan); + void draw(SkCanvas* canvas, const DrawData& drawData) const; + void set_component_list_callbacks(DrawingProgramLayerManager& layerMan) const; + void commit_update_dont_invalidate_cache(DrawingProgramLayerManager& layerMan) const; + void get_flattened_component_list(std::vector& objList) const; NetworkingObjects::NetObjOwnerPtr> folderList; bool isFolderOpen = false; diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerListItem.cpp b/src/DrawingProgram/Layers/DrawingProgramLayerListItem.cpp index 92a3f11..4403648 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayerListItem.cpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayerListItem.cpp @@ -46,6 +46,13 @@ void DrawingProgramLayerListItem::commit_update_dont_invalidate_cache(DrawingPro layerData->commit_update_dont_invalidate_cache(layerMan); } +void DrawingProgramLayerListItem::get_flattened_component_list(std::vector& objList) const { + if(folderData) + folderData->get_flattened_component_list(objList); + else + layerData->get_flattened_component_list(objList); +} + bool DrawingProgramLayerListItem::is_folder() const { return folderData != nullptr; } diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerListItem.hpp b/src/DrawingProgram/Layers/DrawingProgramLayerListItem.hpp index d5f7072..eadd987 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayerListItem.hpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayerListItem.hpp @@ -5,6 +5,7 @@ #include "DrawingProgramLayerFolder.hpp" #include "DrawingProgramLayer.hpp" #include "SerializedBlendMode.hpp" +#include "../../CanvasComponents/CanvasComponentContainer.hpp" class DrawingProgramLayerFolder; class DrawingProgramLayer; @@ -23,9 +24,9 @@ class DrawingProgramLayerListItem { void set_component_list_callbacks(DrawingProgramLayerManager &layerMan); void commit_update_dont_invalidate_cache(DrawingProgramLayerManager& layerMan); void draw(SkCanvas* canvas, const DrawData& drawData) const; - void set_name(NetworkingObjects::DelayUpdateSerializedClassManager& delayedNetObjMan, const std::string& newName) const; const std::string& get_name() const; + void get_flattened_component_list(std::vector& objList) const; void set_alpha(NetworkingObjects::DelayUpdateSerializedClassManager& delayedNetObjMan, float newAlpha) const; float get_alpha() const; diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerManager.cpp b/src/DrawingProgram/Layers/DrawingProgramLayerManager.cpp index b36e8f2..92e3993 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayerManager.cpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayerManager.cpp @@ -32,3 +32,9 @@ void DrawingProgramLayerManager::draw(SkCanvas* canvas, const DrawData& drawData if(layerTreeRoot) layerTreeRoot->draw(canvas, drawData); } + +std::vector DrawingProgramLayerManager::get_flattened_component_list() const { + std::vector toRet; + layerTreeRoot->get_flattened_component_list(toRet); + return toRet; +} diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerManager.hpp b/src/DrawingProgram/Layers/DrawingProgramLayerManager.hpp index 5fdcb4b..a01e16e 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayerManager.hpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayerManager.hpp @@ -25,5 +25,6 @@ class DrawingProgramLayerManager { return nullptr; return editLayerPtr->get_layer().components->push_back_and_send_create(editLayerPtr->get_layer().components, newObj); } + std::vector get_flattened_component_list() const; DrawingProgramLayerManagerGUI listGUI; }; diff --git a/src/DrawingProgram/Tools/EllipseDrawTool.cpp b/src/DrawingProgram/Tools/EllipseDrawTool.cpp index 153f3d7..73bf4b9 100644 --- a/src/DrawingProgram/Tools/EllipseDrawTool.cpp +++ b/src/DrawingProgram/Tools/EllipseDrawTool.cpp @@ -49,7 +49,7 @@ void EllipseDrawTool::switch_tool(DrawingProgramToolType newTool) { void EllipseDrawTool::tool_update() { switch(drawStage) { case 0: { - if(drawP.controls.leftClick) { + if(drawP.controls.leftClick && drawP.layerMan.is_a_layer_being_edited()) { CanvasComponentContainer* newContainer = new CanvasComponentContainer(drawP.world.netObjMan, CanvasComponentType::ELLIPSE); EllipseCanvasComponent& newEllipse = static_cast(newContainer->get_comp()); @@ -63,7 +63,7 @@ void EllipseDrawTool::tool_update() { newEllipse.d.fillStrokeMode = static_cast(fillStrokeMode); newContainer->coords = drawP.world.drawData.cam.c; - objInfoBeingEdited = drawP.components->push_back_and_send_create(drawP.components, newContainer); + objInfoBeingEdited = drawP.layerMan.add_component_to_layer_being_edited(newContainer); drawStage = 1; } break; diff --git a/src/DrawingProgram/Tools/RectDrawTool.cpp b/src/DrawingProgram/Tools/RectDrawTool.cpp index 1ff9070..ecac899 100644 --- a/src/DrawingProgram/Tools/RectDrawTool.cpp +++ b/src/DrawingProgram/Tools/RectDrawTool.cpp @@ -48,7 +48,7 @@ void RectDrawTool::switch_tool(DrawingProgramToolType newTool) { void RectDrawTool::tool_update() { switch(drawStage) { case 0: { - if(drawP.controls.leftClick) { + if(drawP.controls.leftClick && drawP.layerMan.is_a_layer_being_edited()) { CanvasComponentContainer* newContainer = new CanvasComponentContainer(drawP.world.netObjMan, CanvasComponentType::RECTANGLE); RectangleCanvasComponent& newRectangle = static_cast(newContainer->get_comp()); @@ -62,7 +62,7 @@ void RectDrawTool::tool_update() { newRectangle.d.p2 = ensure_points_have_distance(newRectangle.d.p1, newRectangle.d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS); newRectangle.d.fillStrokeMode = static_cast(fillStrokeMode); newContainer->coords = drawP.world.drawData.cam.c; - objInfoBeingEdited = drawP.components->push_back_and_send_create(drawP.components, newContainer); + objInfoBeingEdited = drawP.layerMan.add_component_to_layer_being_edited(newContainer); drawStage = 1; } break; diff --git a/src/DrawingProgram/Tools/TextBoxTool.cpp b/src/DrawingProgram/Tools/TextBoxTool.cpp index 3bde9f3..ef2f807 100644 --- a/src/DrawingProgram/Tools/TextBoxTool.cpp +++ b/src/DrawingProgram/Tools/TextBoxTool.cpp @@ -47,7 +47,7 @@ void TextBoxTool::switch_tool(DrawingProgramToolType newTool) { void TextBoxTool::tool_update() { switch(drawStage) { case 0: { - if(drawP.controls.leftClick) { + if(drawP.controls.leftClick && drawP.layerMan.is_a_layer_being_edited()) { startAt = drawP.world.main.input.mouse.pos; endAt = startAt; @@ -58,7 +58,7 @@ void TextBoxTool::tool_update() { newTextBox.d.p2 = ensure_points_have_distance(newTextBox.d.p1, newTextBox.d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS); newTextBox.d.editing = true; newContainer->coords = drawP.world.drawData.cam.c; - objInfoBeingEdited = drawP.components->push_back_and_send_create(drawP.components, newContainer); + objInfoBeingEdited = drawP.layerMan.add_component_to_layer_being_edited(newContainer); drawStage = 1; } break;