// Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors. // All rights reserved. // Code licensed under the BSD License. // http://www.anki3d.org/LICENSE #include #include #include #include #include #include #include #include #include #include #include #include #include namespace anki { const U NODE_UPDATE_BATCH = 10; class SceneGraph::UpdateSceneNodesCtx { public: SceneGraph* m_scene = nullptr; IntrusiveList::Iterator m_crntNode; SpinLock m_crntNodeLock; Second m_prevUpdateTime; Second m_crntTime; }; SceneGraph::SceneGraph() { } SceneGraph::~SceneGraph() { Error err = iterateSceneNodes([&](SceneNode& s) -> Error { s.setMarkedForDeletion(); return Error::NONE; }); (void)err; deleteNodesMarkedForDeletion(); if(m_octree) { m_alloc.deleteInstance(m_octree); } } Error SceneGraph::init(AllocAlignedCallback allocCb, void* allocCbData, ThreadHive* threadHive, ResourceManager* resources, Input* input, ScriptManager* scriptManager, UiManager* uiManager, const Timestamp* globalTimestamp, const ConfigSet& config) { m_globalTimestamp = globalTimestamp; m_threadHive = threadHive; m_resources = resources; m_gr = &m_resources->getGrManager(); m_physics = &m_resources->getPhysicsWorld(); m_input = input; m_scriptManager = scriptManager; m_uiManager = uiManager; m_alloc = SceneAllocator(allocCb, allocCbData); m_frameAlloc = SceneFrameAllocator(allocCb, allocCbData, 1 * 1024 * 1024); // Limits & stuff m_config.m_earlyZDistance = config.getNumberF32("scene_earlyZDistance"); m_config.m_reflectionProbeEffectiveDistance = config.getNumberF32("scene_reflectionProbeEffectiveDistance"); m_config.m_reflectionProbeShadowEffectiveDistance = config.getNumberF32("scene_reflectionProbeShadowEffectiveDistance"); m_config.m_rayTracedShadows = config.getBool("scene_rayTracedShadows") && m_gr->getDeviceCapabilities().m_rayTracingEnabled; m_config.m_rayTracingExtendedFrustumDistance = config.getNumberF32("scene_rayTracingExtendedFrustumDistance"); m_config.m_maxLodDistances[0] = config.getNumberF32("lod0MaxDistance"); m_config.m_maxLodDistances[1] = config.getNumberF32("lod1MaxDistance"); ANKI_CHECK(m_events.init(this)); m_octree = m_alloc.newInstance(m_alloc); m_octree->init(m_sceneMin, m_sceneMax, config.getNumberU32("scene_octreeMaxDepth")); // Init the default main camera ANKI_CHECK(newSceneNode("mainCamera", m_defaultMainCam)); m_defaultMainCam->getFirstComponentOfType().setPerspective(0.1f, 1000.0f, toRad(60.0f), (1080.0f / 1920.0f) * toRad(60.0f)); m_mainCam = m_defaultMainCam; // Create a special node for debugging the physics world PhysicsDebugNode* pnode; ANKI_CHECK(newSceneNode("_physicsDebugNode", pnode)); // Other ANKI_CHECK(m_debugDrawer.init(&getResourceManager())); return Error::NONE; } Error SceneGraph::registerNode(SceneNode* node) { ANKI_ASSERT(node); // Add to dict if it has a name if(node->getName()) { if(tryFindSceneNode(node->getName())) { ANKI_SCENE_LOGE("Node with the same name already exists"); return Error::USER_DATA; } m_nodesDict.emplace(m_alloc, node->getName(), node); } // Add to vector m_nodes.pushBack(node); ++m_nodesCount; return Error::NONE; } void SceneGraph::unregisterNode(SceneNode* node) { // Remove from the graph m_nodes.erase(node); --m_nodesCount; if(m_mainCam != m_defaultMainCam && m_mainCam == node) { m_mainCam = m_defaultMainCam; } // Remove from dict if(node->getName()) { auto it = m_nodesDict.find(node->getName()); ANKI_ASSERT(it != m_nodesDict.getEnd()); m_nodesDict.erase(m_alloc, it); } } SceneNode& SceneGraph::findSceneNode(const CString& name) { SceneNode* node = tryFindSceneNode(name); ANKI_ASSERT(node); return *node; } SceneNode* SceneGraph::tryFindSceneNode(const CString& name) { auto it = m_nodesDict.find(name); return (it == m_nodesDict.getEnd()) ? nullptr : (*it); } void SceneGraph::deleteNodesMarkedForDeletion() { /// Delete all nodes pending deletion. At this point all scene threads /// should have finished their tasks while(m_objectsMarkedForDeletionCount.load() > 0) { Bool found = false; auto it = m_nodes.begin(); auto end = m_nodes.end(); for(; it != end; ++it) { SceneNode& node = *it; if(node.getMarkedForDeletion()) { // Delete node unregisterNode(&node); m_alloc.deleteInstance(&node); m_objectsMarkedForDeletionCount.fetchSub(1); found = true; break; } } (void)found; ANKI_ASSERT(found && "Something is wrong with marked for deletion"); } } Error SceneGraph::update(Second prevUpdateTime, Second crntTime) { ANKI_ASSERT(m_mainCam); ANKI_TRACE_SCOPED_EVENT(SCENE_UPDATE); m_stats.m_updateTime = HighRezTimer::getCurrentTime(); m_timestamp = *m_globalTimestamp; ANKI_ASSERT(m_timestamp > 0); // Reset the framepool m_frameAlloc.getMemoryPool().reset(); // Delete stuff { ANKI_TRACE_SCOPED_EVENT(SCENE_MARKED_FOR_DELETION); const Bool fullCleanup = m_objectsMarkedForDeletionCount.load() != 0; m_events.deleteEventsMarkedForDeletion(fullCleanup); deleteNodesMarkedForDeletion(); } // Update { ANKI_TRACE_SCOPED_EVENT(SCENE_PHYSICS_UPDATE); m_stats.m_physicsUpdate = HighRezTimer::getCurrentTime(); m_physics->update(crntTime - prevUpdateTime); m_stats.m_physicsUpdate = HighRezTimer::getCurrentTime() - m_stats.m_physicsUpdate; } { ANKI_TRACE_SCOPED_EVENT(SCENE_NODES_UPDATE); ANKI_CHECK(m_events.updateAllEvents(prevUpdateTime, crntTime)); // Then the rest Array tasks; UpdateSceneNodesCtx updateCtx; updateCtx.m_scene = this; updateCtx.m_crntNode = m_nodes.getBegin(); updateCtx.m_prevUpdateTime = prevUpdateTime; updateCtx.m_crntTime = crntTime; for(U i = 0; i < m_threadHive->getThreadCount(); i++) { tasks[i] = ANKI_THREAD_HIVE_TASK( { if(self->m_scene->updateNodes(*self)) { ANKI_SCENE_LOGF("Will not recover"); } }, &updateCtx, nullptr, nullptr); } m_threadHive->submitTasks(&tasks[0], m_threadHive->getThreadCount()); m_threadHive->waitAllTasks(); } m_stats.m_updateTime = HighRezTimer::getCurrentTime() - m_stats.m_updateTime; return Error::NONE; } void SceneGraph::doVisibilityTests(RenderQueue& rqueue) { m_stats.m_visibilityTestsTime = HighRezTimer::getCurrentTime(); doVisibilityTests(*m_mainCam, *this, rqueue); m_stats.m_visibilityTestsTime = HighRezTimer::getCurrentTime() - m_stats.m_visibilityTestsTime; } Error SceneGraph::updateNode(Second prevTime, Second crntTime, SceneNode& node) { ANKI_TRACE_INC_COUNTER(SCENE_NODES_UPDATED, 1); Error err = Error::NONE; // Components update Timestamp componentTimestamp = 0; Bool atLeastOneComponentUpdated = false; err = node.iterateComponents([&](SceneComponent& comp, Bool isFeedbackComponent) -> Error { Bool updated = false; Error e = Error::NONE; if(!atLeastOneComponentUpdated && isFeedbackComponent) { // Skip feedback component if prior components didn't got updated } else { e = comp.update(node, prevTime, crntTime, updated); } if(updated) { ANKI_TRACE_INC_COUNTER(SCENE_COMPONENTS_UPDATED, 1); comp.setTimestamp(node.getSceneGraph().m_timestamp); componentTimestamp = max(componentTimestamp, node.getSceneGraph().m_timestamp); ANKI_ASSERT(componentTimestamp > 0); atLeastOneComponentUpdated = true; } return e; }); // Update children if(!err) { err = node.visitChildrenMaxDepth(0, [&](SceneNode& child) -> Error { return updateNode(prevTime, crntTime, child); }); } // Frame update if(!err) { if(componentTimestamp != 0) { node.setComponentMaxTimestamp(componentTimestamp); } else { // No components or nothing updated, don't change the timestamp } err = node.frameUpdate(prevTime, crntTime); } return err; } Error SceneGraph::updateNodes(UpdateSceneNodesCtx& ctx) const { ANKI_TRACE_SCOPED_EVENT(SCENE_NODES_UPDATE); IntrusiveList::ConstIterator end = m_nodes.getEnd(); Bool quit = false; Error err = Error::NONE; while(!quit && !err) { // Fetch a batch of scene nodes that don't have parent Array batch; U batchSize = 0; { LockGuard lock(ctx.m_crntNodeLock); while(1) { if(batchSize == batch.getSize()) { break; } if(ctx.m_crntNode == end) { quit = true; break; } SceneNode& node = *ctx.m_crntNode; if(node.getParent() == nullptr) { batch[batchSize++] = &node; } ++ctx.m_crntNode; } } // Process nodes for(U i = 0; i < batchSize && !err; ++i) { err = updateNode(ctx.m_prevUpdateTime, ctx.m_crntTime, *batch[i]); } } return err; } } // end namespace anki