// sorted list - rendering queue order // sorted list of refferences according to the screen size // - analyze only second one // - remove skip field from the Struct void OrderEstimator::compute( RenderNodeVec& renderNodes, NodeIdPosVec& desiredIds, const GPUCacheManager& gpuManager, const VolumeTreeBase& tree, const Box_f& bb, const CameraParameters& cp, const uint32_t nodesMax, const uint32_t tfHist, const float msMax, const uint8_t maxTreeDepth, const bool frontToBack, bool useRenderingError, const uint16_t renderingError ) { renderNodes.resize( 0 ); desiredIds.resize( 0 ); // Check if buget is allocated if( nodesMax < 1 ) { LBWARN << "budget is zero" << std::endl; return; } // Check if tree is initialized const uint32_t rootNodePos = 0; const NodeId rootNodeId = tree.getNodeId( rootNodePos ); if( tree.getSize() < 1 || rootNodeId < 1 ) { LBWARN << "octree is not initialized" << std::endl; return; } const VolumeTreeTensorErrors* treeErr = dynamic_cast< const VolumeTreeTensorErrors* >(&tree); if( !treeErr ) useRenderingError = false; byte rank = _getRank( _tensorPrms->getRank(0), useRenderingError, renderingError, 0, treeErr ); // Check if root node is loaded if( !gpuManager.hasNodeOnGPU( rootNodeId )) { // always keep root node loaded desiredIds.push_back( NodeIdPos( rootNodeId, rootNodePos, rank )); return; } // Initialize rendering queue with the root node and // then try to expand it RenderNode rNode; rNode.nodeId = rootNodeId; rNode.coords = _getFloatDataPos( tree ); rNode.treePos = rootNodePos; rNode.treeLevel = 1; rNode.screenRect = cp.computeScreenProjectionRect( rNode.coords ); rNode.screenRectSquare = rNode.screenRect.getAreaSize(); rNode.screenSize = rNode.screenRect.getDiagonalSize(); rNode.renderingTime = _estimateRenderingTime( rNode.screenSize ); rNode.rank = rank; rNode.fullyVsible = cp.computeVisibility( rNode.coords ) == vmml::VISIBILITY_FULL; std::list<RenderNode> renderList; std::vector<RenderNodeListIt> renderHeap; renderList.push_front( rNode ); renderHeap.push_back( renderList.begin() ); std::make_heap( renderHeap.begin(), renderHeap.end(), RenderNodeListItCmp ); uint32_t nodesUsed = 1; float timeUsed = rNode.renderingTime; // blocks to render stay in renderList, if expansion is desired but // not yet possible (data is not on GPU) corresponding requests are // copied to desiredIds array. RenderNode childrenNodes[8]; Box_f quads[8]; OctreeChildrenOrder childrenOrder; bool expanded = true; while( expanded && nodesUsed+8 < nodesMax && timeUsed < msMax ) { expanded = false; while( renderHeap.size() > 0 ) { const RenderNode* current = &(*renderHeap.front()); if( current->treeLevel >= maxTreeDepth ) { pop_heap( renderHeap.begin(), renderHeap.end(), RenderNodeListItCmp ); renderHeap.pop_back(); continue; } if( !tree.hasVisibleData( current->treePos, tfHist )) { pop_heap( renderHeap.begin(), renderHeap.end(), RenderNodeListItCmp ); renderHeap.pop_back(); continue; } // if leaf node - don't expand, skip in the future if( !tree.hasChildren( current->treePos )) { pop_heap( renderHeap.begin(), renderHeap.end(), RenderNodeListItCmp ); renderHeap.pop_back(); continue; } // Check that resolution is sufficient // LBWARN << "nS: " << current->screenSize << " tS: " << tree.getBlockSize() << std::endl; bool screenSizeValid = static_cast<int>( current->screenSize )/2.8 < tree.getBlockSize(); // if( screenSizeValid ) if( _validResolution( screenSizeValid, useRenderingError, renderingError, current->treePos, treeErr )) { pop_heap( renderHeap.begin(), renderHeap.end(), RenderNodeListItCmp ); renderHeap.pop_back(); continue; } // node is expandable: // - check how much it costs to expand (only withing view frustum), proceed if enougth resources: // - if all children that are in the view frustum are also on GPU, then replace root (put root to desired) _getChildrenOrder( childrenOrder, current->coords, cp ); current->coords.getQuadrants( quads ); const int64_t childrenPos = tree.getChild( current->treePos ); uint32_t count = 0; // visible child nodes uint32_t countGPU = 0; // visible child nodes on GPU for( int i = 0; i < 8; ++i ) { const uint32_t childPos = childrenPos + childrenOrder.pos[i]; const uint32_t childId = tree.getNodeId( childPos ); if( childId == 0 ) // empty node continue; if( !tree.hasVisibleData( childPos, tfHist )) continue; RenderNode& node = childrenNodes[ count ]; node.nodeId = childId; node.coords = quads[ childrenOrder.pos[i] ]; node.treePos = childPos; // check if child fits to required data BB if( !bb.intersect( node.coords ).valid() ) continue; // check if child is visible if( current->fullyVsible ) { node.fullyVsible = true; }else { const vmml::Visibility visibility = cp.computeVisibility( node.coords ); if( visibility == vmml::VISIBILITY_NONE ) continue; node.fullyVsible = (visibility == vmml::VISIBILITY_FULL); } // check if child is on GPU if( gpuManager.hasNodeOnGPU( node.nodeId )) countGPU++; count++; } if( count == 0 ) // no valid children { pop_heap( renderHeap.begin(), renderHeap.end(), RenderNodeListItCmp ); renderHeap.pop_back(); continue; } if( nodesUsed + count - 1 > nodesMax ) // too many nodes already { pop_heap( renderHeap.begin(), renderHeap.end(), RenderNodeListItCmp ); renderHeap.pop_back(); continue; } float childrenRenderingTime = 0; if( count == countGPU )// everything is on GPU, compute time { for( uint32_t i = 0; i < count; ++i ) { RenderNode& node = childrenNodes[ i ]; node.screenRect = cp.computeScreenProjectionRect( node.coords ); node.screenRectSquare = node.screenRect.getAreaSize(); node.screenSize = node.screenRect.getDiagonalSize(); node.renderingTime = _estimateRenderingTime( node.screenSize ); LBASSERT( current->treeLevel > 0 ); node.treeLevel = current->treeLevel + 1; node.rank = _getRank( _tensorPrms->getRank(current->treeLevel-1), useRenderingError, renderingError, node.treePos, treeErr ); childrenRenderingTime += node.renderingTime; if( timeUsed - current->renderingTime + childrenRenderingTime > msMax ) break; } } // we will add all children to loading or rendering queue now nodesUsed += count; // adjust budget bool childrenQualityTooHigh = useRenderingError; if( useRenderingError ) { for( uint32_t i = 0; i < count; ++i ) if( !treeErr->isQualityTooHigh( childrenNodes[i].treePos, renderingError )) { childrenQualityTooHigh = false; break; } } if( count != countGPU || // some children are not on GPU or rendering of children would take too long timeUsed - current->renderingTime + childrenRenderingTime > msMax || childrenQualityTooHigh ) { // scedule children for loading for( uint32_t i = 0; i < count; ++i ) desiredIds.push_back( NodeIdPos( childrenNodes[i].nodeId, childrenNodes[i].treePos, childrenNodes[i].rank )); pop_heap( renderHeap.begin(), renderHeap.end(), RenderNodeListItCmp ); renderHeap.pop_back(); continue; } // will render children instead of the root // adjust used time timeUsed = timeUsed - current->renderingTime + childrenRenderingTime; // replace root with children desiredIds.push_back( NodeIdPos( current->nodeId, current->treePos, current->rank )); // replace root with first child RenderNodeListIt parentId = renderHeap.front(); pop_heap( renderHeap.begin(), renderHeap.end(), RenderNodeListItCmp ); *parentId = childrenNodes[ 0 ]; push_heap( renderHeap.begin(), renderHeap.end(), RenderNodeListItCmp ); ++parentId; // insert other children for( uint32_t i = 1; i < count; ++i ) { RenderNodeListIt childId = renderList.insert( parentId, childrenNodes[ i ] ); renderHeap.push_back( childId ); push_heap( renderHeap.begin(), renderHeap.end(), RenderNodeListItCmp ); } expanded = true; if( timeUsed >= msMax || nodesUsed+8 >= nodesMax ) break; } } // now renderList has desired set of blocks, we copy rendering front to // the destination storage "renderNodes", additionally node are Ids duplicated to // desiredIds array to keep them on GPU if( frontToBack ) { for( RenderNodeListIt it = renderList.begin(); it != renderList.end(); ++it ) { RenderNode* current = &(*it); renderNodes.push_back( *current ); desiredIds.push_back( NodeIdPos( current->nodeId, current->treePos, current->rank )); } }else { RenderNodeListIt it = renderList.end(); while( it != renderList.begin() ) { --it; RenderNode* current = &(*it); renderNodes.push_back( *current ); desiredIds.push_back( NodeIdPos( current->nodeId, current->treePos, current->rank )); } } }