diff --git a/src/CollabList.hpp b/src/CollabList.hpp index 271b7cd..d048f49 100644 --- a/src/CollabList.hpp +++ b/src/CollabList.hpp @@ -77,6 +77,8 @@ template class CollabList { newObj->id = getNewIDFunc(); newObj->obj = item; newObj->pos = std::min(static_cast(clientSideList.size()), pos); + item->collabListInfo = newObj; + clientSideList.insert(clientSideList.begin() + newObj->pos, newObj); client_pos_refresh_after_insert(newObj->pos); @@ -123,6 +125,8 @@ template class CollabList { newObj->id = id; newObj->obj = item; newObj->pos = std::min(pos, clientSideList.size()); + item->collabListInfo = newObj; + serverSideList.insert(serverSideList.begin() + pos, newObj); clientSideList.insert(clientSideList.begin() + newObj->pos, newObj); @@ -160,6 +164,7 @@ template class CollabList { newObj->id = id; newObj->obj = item; newObj->pos = clientSideList.size() - 1; + item->collabListInfo = newObj; serverSideList.emplace_back(newObj); clientSideList.emplace_back(newObj); diff --git a/src/DrawComponents/DrawComponent.cpp b/src/DrawComponents/DrawComponent.cpp index 4d14203..893b2ff 100644 --- a/src/DrawComponents/DrawComponent.cpp +++ b/src/DrawComponents/DrawComponent.cpp @@ -109,14 +109,14 @@ void DrawComponent::check_timers(DrawingProgram& drawP) { } void DrawComponent::temp_update(DrawingProgram& drawP) { - drawP.compCache.preupdate_component(this); + drawP.compCache.preupdate_component(collabListInfo.lock()); worldAABB = std::nullopt; initialize_draw_data(drawP); updateDraw = true; } void DrawComponent::transform_temp_update(DrawingProgram& drawP) { - drawP.compCache.preupdate_component(this); + drawP.compCache.preupdate_component(collabListInfo.lock()); worldAABB = std::nullopt; updateDraw = true; } @@ -127,12 +127,6 @@ 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); } @@ -159,7 +153,7 @@ void DrawComponent::client_send_transform_final(DrawingProgram& drawP, ServerCli void DrawComponent::finalize_update(DrawingProgram& drawP, bool invalidateCache) { if(invalidateCache) - drawP.compCache.preupdate_component(this); + drawP.compCache.preupdate_component(collabListInfo.lock()); create_collider(drawP.colliderAllocated); updateDraw = true; } diff --git a/src/DrawComponents/DrawComponent.hpp b/src/DrawComponents/DrawComponent.hpp index 9799b9f..731ffff 100644 --- a/src/DrawComponents/DrawComponent.hpp +++ b/src/DrawComponents/DrawComponent.hpp @@ -5,6 +5,8 @@ #include "../SharedTypes.hpp" #include "../CoordSpaceHelper.hpp" #include "../Server/MainServer.hpp" +#include "../CollabList.hpp" +#include "../DrawingProgram/DrawingProgramCache.hpp" #ifndef IS_SERVER #include "../DrawData.hpp" @@ -64,6 +66,9 @@ class DrawComponent { std::chrono::time_point tempServerTransformTimer; bool serverIsTempTransform = false; + std::weak_ptr, ServerClientID>::ObjectInfo> collabListInfo; + std::weak_ptr parentBvhNode; + void server_update(MainServer& server, ServerClientID id); #ifndef IS_SERVER @@ -100,7 +105,6 @@ 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, bool invalidateCache = true); diff --git a/src/DrawingProgram/BrushTool.cpp b/src/DrawingProgram/BrushTool.cpp index 5d2f754..628083a 100644 --- a/src/DrawingProgram/BrushTool.cpp +++ b/src/DrawingProgram/BrushTool.cpp @@ -106,9 +106,9 @@ void BrushTool::tool_update() { controls.intermediateItem->d.hasRoundCaps = controls.hasRoundCaps; controls.intermediateItem->coords = drawP.world.drawData.cam.c; controls.intermediateItem->lastUpdateTime = std::chrono::steady_clock::now(); - controls.intermediateItem->temp_update(drawP); uint64_t placement = drawP.components.client_list().size(); controls.intermediateID = drawP.components.client_insert(placement, controls.intermediateItem); + controls.intermediateItem->temp_update(drawP); controls.intermediateItem->client_send_place(drawP, placement); // NOTE: The data isnt finallized at this point, so a BVH isn't generated for an object that you undo while youre drawing (this isnt just for brush strokes) // The fix for now is to generate a BVH for an object when you redo it diff --git a/src/DrawingProgram/DrawingProgram.cpp b/src/DrawingProgram/DrawingProgram.cpp index 67d3c4a..529d776 100644 --- a/src/DrawingProgram/DrawingProgram.cpp +++ b/src/DrawingProgram/DrawingProgram.cpp @@ -146,7 +146,7 @@ void DrawingProgram::parallel_loop_all_components(std::function& checkAgainstWorld, const SCollision::ColliderCollection& checkAgainstCam) { for(auto& c : components.client_list()) c->obj->globalCollisionCheck = false; - compCache.traverse_bvh_run_function(checkAgainstWorld.bounds, [&](DrawingProgramCache::BVHNode* node, const std::vector& comps) { + compCache.traverse_bvh_run_function(checkAgainstWorld.bounds, [&](const std::shared_ptr& node, const std::vector& comps) { for(auto& c : comps) c->obj->globalCollisionCheck = c->obj->collides_with(world.drawData.cam.c, checkAgainstWorld, checkAgainstCam, colliderAllocated); return true; @@ -447,8 +447,9 @@ 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_dont_invalidate_cache(*this); + newComp->final_update(*this); } + compCache.force_rebuild(); } void DrawingProgram::reset_selection() { @@ -607,9 +608,9 @@ void DrawingProgram::draw_components_to_canvas(SkCanvas* canvas, const DrawData& for(auto& c : components.client_list()) c->obj->drawSetupData.shouldDraw = false; - std::vector cachedNodesToDraw; + std::vector> cachedNodesToDraw; - compCache.traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](DrawingProgramCache::BVHNode* node, const std::vector& comps) { + compCache.traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](const std::shared_ptr& node, const std::vector& comps) { if(!dontUseCache && node && node->drawCache && node->coords.inverseScale <= drawData.cam.c.inverseScale) { cachedNodesToDraw.emplace_back(node); return false; @@ -622,7 +623,7 @@ void DrawingProgram::draw_components_to_canvas(SkCanvas* canvas, const DrawData& 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) { + for(const std::shared_ptr& bvhNode : cachedNodesToDraw) { auto& drawCache = bvhNode->drawCache.value(); if(drawCache.lastDrawnComponentPlacement == i) { canvas->save(); diff --git a/src/DrawingProgram/DrawingProgramCache.cpp b/src/DrawingProgram/DrawingProgramCache.cpp index d7bb51a..ed26e5e 100644 --- a/src/DrawingProgram/DrawingProgramCache.cpp +++ b/src/DrawingProgram/DrawingProgramCache.cpp @@ -15,25 +15,33 @@ DrawingProgramCache::DrawingProgramCache(DrawingProgram& initDrawP): drawP(initDrawP) -{} +{ + lastBvhBuildTime = std::chrono::steady_clock::now(); +} void DrawingProgramCache::build(std::vector components) { - bvhRoot = BVHNode(); + bvhRoot = std::make_shared(); unsortedComponents.clear(); nodesWithCachedSurfaces.clear(); std::erase_if(components, [&](auto& c) { if(!c->obj->worldAABB || c->obj->updateDraw) { unsortedComponents.emplace_back(c); + c->obj->parentBvhNode.reset(); return true; } return false; }); build_bvh_node(bvhRoot, components); + lastBvhBuildTime = std::chrono::steady_clock::now(); } void DrawingProgramCache::update() { - if(unsortedComponents.size() >= 750) - build(drawP.components.client_list()); + if(unsortedComponents.size() >= 750 && (std::chrono::steady_clock::now() - lastBvhBuildTime) >= std::chrono::seconds(7)) + force_rebuild(); +} + +void DrawingProgramCache::force_rebuild() { + build(drawP.components.client_list()); } void DrawingProgramCache::add_component(const CollabListType::ObjectInfoPtr& c) { @@ -42,84 +50,73 @@ void DrawingProgramCache::add_component(const CollabListType::ObjectInfoPtr& c) } void DrawingProgramCache::erase_component(const CollabListType::ObjectInfoPtr& c) { - auto it = std::find(unsortedComponents.begin(), unsortedComponents.end(), c); - if(it != unsortedComponents.end()) - unsortedComponents.erase(it); - else { - if(!c->obj->worldAABB.has_value()) - return; - auto bvhNodePair = get_bvh_node_fully_containing_recursive(bvhRoot, c->obj.get()); - if(bvhNodePair.first) - bvhNodePair.first->components.erase(bvhNodePair.second); + auto parentBvhNode = c->obj->parentBvhNode.lock(); + if(parentBvhNode) { + std::erase(parentBvhNode->components, c); + c->obj->parentBvhNode.reset(); } + else + std::erase(unsortedComponents, c); invalidate_cache_before_pos(c->pos); } -std::pair::iterator> DrawingProgramCache::get_bvh_node_fully_containing_recursive(BVHNode& bvhNode, DrawComponent* c) { +std::pair, std::vector::iterator> DrawingProgramCache::get_bvh_node_fully_containing_recursive(const std::shared_ptr& bvhNode, DrawComponent* c) { SCollision::AABB& cAABB = c->worldAABB.value(); - if(!bvhNode.bounds.fully_contains_aabb(cAABB)) - return {nullptr, bvhNode.components.end()}; - auto it = std::find_if(bvhNode.components.begin(), bvhNode.components.end(), [&](const auto& comp) { + if(!bvhNode->bounds.fully_contains_aabb(cAABB)) + return {nullptr, bvhNode->components.end()}; + auto it = std::find_if(bvhNode->components.begin(), bvhNode->components.end(), [&](const auto& comp) { return comp->obj.get() == c; }); - if(it != bvhNode.components.end()) - return {&bvhNode, it}; - for(auto& p : bvhNode.children) { + if(it != bvhNode->components.end()) + return {bvhNode, it}; + for(auto& p : bvhNode->children) { auto toRet = get_bvh_node_fully_containing_recursive(p, c); if(toRet.first) return toRet; } - return {nullptr, bvhNode.components.end()}; + return {nullptr, bvhNode->components.end()}; } -void DrawingProgramCache::traverse_bvh_run_function(const SCollision::AABB& aabb, std::function& components)> f) { +void DrawingProgramCache::traverse_bvh_run_function(const SCollision::AABB& aabb, std::function node, const std::vector& components)> f) { f(nullptr, unsortedComponents); traverse_bvh_run_function_recursive(bvhRoot, aabb, f); } -void DrawingProgramCache::traverse_bvh_run_function_recursive(BVHNode& bvhNode, const SCollision::AABB& aabb, std::function& components)> f) { - if(SCollision::collide(aabb, bvhNode.bounds) && f(&bvhNode, bvhNode.components)) { - for(auto& p : bvhNode.children) +void DrawingProgramCache::traverse_bvh_run_function_recursive(const std::shared_ptr& bvhNode, const SCollision::AABB& aabb, std::function& node, const std::vector& components)> f) { + if(bvhNode && SCollision::collide(aabb, bvhNode->bounds) && f(bvhNode, bvhNode->components)) { + for(auto& p : bvhNode->children) traverse_bvh_run_function_recursive(p, aabb, f); } } -void DrawingProgramCache::preupdate_component(DrawComponent* c) { - auto it = std::find_if(unsortedComponents.begin(), unsortedComponents.end(), [&](const auto& comp) { - return comp->obj.get() == c; - }); - if(it != unsortedComponents.end()) { - invalidate_cache_before_pos((*it)->pos); - return; - } - else { - if(!c->worldAABB.has_value()) - return; - auto bvhNodePair = get_bvh_node_fully_containing_recursive(bvhRoot, c); - if(bvhNodePair.first) { - unsortedComponents.emplace_back(*bvhNodePair.second); - invalidate_cache_before_pos((*bvhNodePair.second)->pos); - bvhNodePair.first->components.erase(bvhNodePair.second); - } +void DrawingProgramCache::preupdate_component(const CollabListType::ObjectInfoPtr& c) { + auto parentBvhNode = c->obj->parentBvhNode.lock(); + if(parentBvhNode) { + unsortedComponents.emplace_back(c); + std::erase(parentBvhNode->components, c); + c->obj->parentBvhNode.reset(); } + invalidate_cache_before_pos(c->pos); } -void DrawingProgramCache::build_bvh_node(BVHNode& node, std::vector components) { +void DrawingProgramCache::build_bvh_node(const std::shared_ptr& bvhNode, std::vector components) { if(components.empty()) return; - node.bounds = components.front()->obj->worldAABB.value(); + bvhNode->bounds = components.front()->obj->worldAABB.value(); for(auto& c : components) - node.bounds.include_aabb_in_bounds(c->obj->worldAABB.value()); + bvhNode->bounds.include_aabb_in_bounds(c->obj->worldAABB.value()); - build_bvh_node_coords_and_resolution(node); + build_bvh_node_coords_and_resolution(*bvhNode); if(components.size() < 100) { - node.components = components; + bvhNode->components = components; + for(auto& c : bvhNode->components) + c->obj->parentBvhNode = bvhNode; return; } - WorldVec boundsCenter = node.bounds.center(); + WorldVec boundsCenter = bvhNode->bounds.center(); std::array, 4> parts; @@ -133,13 +130,15 @@ void DrawingProgramCache::build_bvh_node(BVHNode& node, std::vector boundsCenter.x() && cAABB.max.x() > boundsCenter.x() && cAABB.min.y() > boundsCenter.y() && cAABB.max.y() > boundsCenter.y()) parts[3].emplace_back(c); - else - node.components.emplace_back(c); + else { + bvhNode->components.emplace_back(c); + c->obj->parentBvhNode = bvhNode; + } } for(auto& p : parts) { if(!p.empty()) - build_bvh_node(node.children.emplace_back(), p); + build_bvh_node(bvhNode->children.emplace_back(std::make_shared()), p); } } @@ -184,8 +183,8 @@ void DrawingProgramCache::invalidate_cache_before_pos(uint64_t placementToInvali } 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) { + 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) nodeFlatList.emplace_front(node); @@ -194,17 +193,17 @@ void DrawingProgramCache::refresh_all_draw_cache(SkCanvas* canvas, const DrawDat return true; }); if(!nodeFlatList.empty()) - refresh_draw_cache(*nodeFlatList.front(), drawData); + refresh_draw_cache(nodeFlatList.front(), drawData); } -void DrawingProgramCache::refresh_draw_cache(BVHNode& bvhNode, const DrawData& drawData) { - if(bvhNode.children.empty() && bvhNode.components.empty()) +void DrawingProgramCache::refresh_draw_cache(const std::shared_ptr& bvhNode, const DrawData& drawData) { + if(bvhNode->children.empty() && bvhNode->components.empty()) return; - bvhNode.drawCache = std::nullopt; + bvhNode->drawCache = std::nullopt; BVHNode::DrawCacheData drawCache; - SkImageInfo imgInfo = SkImageInfo::MakeN32Premul(bvhNode.resolution.x(), bvhNode.resolution.y()); + 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 @@ -219,25 +218,26 @@ void DrawingProgramCache::refresh_draw_cache(BVHNode& bvhNode, const DrawData& d cacheCanvas->clear(SkColor4f{0, 0, 0, 0}); DrawData cacheDrawData = drawData; - cacheDrawData.cam.c = bvhNode.coords; + cacheDrawData.cam.c = bvhNode->coords; cacheDrawData.cam.changed = nullptr; - cacheDrawData.cam.set_viewing_area(bvhNode.resolution.cast()); + 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 + 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) { + std::shared_ptr leastUsedNode = *nodesWithCachedSurfaces.begin(); + for(const std::shared_ptr& n : nodesWithCachedSurfaces) { if(n->drawCache.value().lastRenderTime < leastUsedNode->drawCache.value().lastRenderTime) leastUsedNode = n; } + leastUsedNode->drawCache = std::nullopt; nodesWithCachedSurfaces.erase(leastUsedNode); } - nodesWithCachedSurfaces.emplace(&bvhNode); + nodesWithCachedSurfaces.emplace(bvhNode); } diff --git a/src/DrawingProgram/DrawingProgramCache.hpp b/src/DrawingProgram/DrawingProgramCache.hpp index 8d9c331..cb29f8d 100644 --- a/src/DrawingProgram/DrawingProgramCache.hpp +++ b/src/DrawingProgram/DrawingProgramCache.hpp @@ -1,12 +1,14 @@ #pragma once #include #include "../CollabList.hpp" -#include "../DrawComponents/DrawComponent.hpp" #include #include +#include "../DrawData.hpp" class DrawingProgram; +class DrawComponent; + class DrawingProgramCache { public: typedef CollabList, ServerClientID> CollabListType; @@ -20,7 +22,7 @@ class DrawingProgramCache { struct BVHNode { SCollision::AABB bounds; std::vector components; - std::vector children; + std::vector> children; CoordSpaceHelper coords; Vector2i resolution; @@ -33,25 +35,28 @@ class DrawingProgramCache { std::optional drawCache; }; - std::unordered_set nodesWithCachedSurfaces; + std::unordered_set> nodesWithCachedSurfaces; - void traverse_bvh_run_function(const SCollision::AABB& aabb, std::function& components)> f); + void traverse_bvh_run_function(const SCollision::AABB& aabb, std::function node, const std::vector& components)> f); + void force_rebuild(); // Run before actually updating the component - void preupdate_component(DrawComponent* c); + void preupdate_component(const CollabListType::ObjectInfoPtr& c); 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 refresh_draw_cache(const std::shared_ptr& bvhNode, const DrawData& drawData); void build(std::vector components); - void traverse_bvh_run_function_recursive(BVHNode& bvhNode, const SCollision::AABB& aabb, std::function& components)> f); - std::pair::iterator> get_bvh_node_fully_containing_recursive(BVHNode& bvhNode, DrawComponent* c); + std::chrono::steady_clock::time_point lastBvhBuildTime; - void build_bvh_node(BVHNode& node, std::vector components); - BVHNode bvhRoot; + void traverse_bvh_run_function_recursive(const std::shared_ptr& bvhNode, const SCollision::AABB& aabb, std::function& node, const std::vector& components)> f); + std::pair, std::vector::iterator> get_bvh_node_fully_containing_recursive(const std::shared_ptr& bvhNode, DrawComponent* c); + + void build_bvh_node(const std::shared_ptr& bvhNode, std::vector components); + std::shared_ptr bvhRoot; std::vector unsortedComponents; DrawingProgram& drawP; }; diff --git a/src/DrawingProgram/EllipseDrawTool.cpp b/src/DrawingProgram/EllipseDrawTool.cpp index dec331e..a499135 100644 --- a/src/DrawingProgram/EllipseDrawTool.cpp +++ b/src/DrawingProgram/EllipseDrawTool.cpp @@ -13,7 +13,6 @@ EllipseDrawTool::EllipseDrawTool(DrawingProgram& initDrawP): drawP(initDrawP) { - controls.intermediateItem = std::make_shared(); } void EllipseDrawTool::gui_toolbox() { diff --git a/src/DrawingProgram/RectDrawTool.cpp b/src/DrawingProgram/RectDrawTool.cpp index 127be21..a3319d8 100644 --- a/src/DrawingProgram/RectDrawTool.cpp +++ b/src/DrawingProgram/RectDrawTool.cpp @@ -10,9 +10,7 @@ RectDrawTool::RectDrawTool(DrawingProgram& initDrawP): drawP(initDrawP) -{ - controls.intermediateItem = std::make_shared(); -} +{} void RectDrawTool::gui_toolbox() { Toolbar& t = drawP.world.main.toolbar; @@ -114,9 +112,9 @@ void RectDrawTool::tool_update() { controls.intermediateItem->d.fillStrokeMode = static_cast(controls.fillStrokeMode); controls.intermediateItem->coords = drawP.world.drawData.cam.c; controls.intermediateItem->lastUpdateTime = std::chrono::steady_clock::now(); - controls.intermediateItem->temp_update(drawP); uint64_t placement = drawP.components.client_list().size(); controls.intermediateID = drawP.components.client_insert(placement, controls.intermediateItem); + controls.intermediateItem->temp_update(drawP); controls.intermediateItem->client_send_place(drawP, placement); drawP.add_undo_place_component(placement, controls.intermediateItem); controls.drawStage = 1;