/**************************************************************************** * VCGLib o o * * Visual and Computer Graphics Library o o * * _ O _ * * Copyright(C) 2004 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * * All rights reserved. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * * for more details. * * * ****************************************************************************/ /**************************************************************************** History $Log: not supported by cvs2svn $ Revision 1.12 2005/03/02 10:40:17 ponchio Extraction rewrittten (to fix recusive problems). Revision 1.11 2005/02/20 19:49:44 ponchio cleaning (a bit more). Revision 1.10 2005/02/20 18:07:00 ponchio cleaning. Revision 1.9 2005/02/20 00:43:23 ponchio Less memory x extraction. (removed frags) Revision 1.8 2005/02/19 16:22:45 ponchio Minor changes (visited and Cell) Revision 1.7 2005/02/10 09:18:20 ponchio Statistics. Revision 1.6 2005/02/08 12:43:03 ponchio Added copyright ****************************************************************************/ #include "extraction.h" #include "metric.h" #include "nexus.h" #include using namespace std; using namespace nxs; /* Updateing strategy: if i can refine (not at leaves, have draw and extr buffer, not past target_error) i try to refine BUT i can fail because i finish some buffer (then i put the operation back on the stack) if i have finished disk i should just quit if i cannot refine i consider coarsening: i need 1) not be at root (eheh) 2) have finished draw and extr buffer (unless i am at error < target so i want to coarse 3) do not make global error worse (unless it is error < target_error...) a stability term is added (1.1) so that we do not flip nodes in and out quickly i try to coarse BUT i can fail because i need disk (then i put the operation back on the stack) if i cannt coarse i just quit */ Extraction::Extraction(): target_error(4.0f), extr_max(10000), draw_max(10000), disk_max(100) { metric = new FrustumMetric; } Extraction::~Extraction() { if(metric) delete metric; } void Extraction::SetMetric(Metric *m) { if(metric) delete metric; metric = m; } void Extraction::Extract(Nexus *_mt) { mt = _mt; root = mt->history.Root(); sink = root + (mt->history.n_nodes()-1); //clear statistics extr_used = draw_used = disk_used = 0; //first we clear the visited flags visited.clear(); visited.resize(mt->history.n_nodes(), false); visible.clear(); visible.resize(mt->size(), true); node_errors.clear(); node_errors.resize(mt->history.n_nodes(), -1); front.clear(); Visit(root); while(front.size()) { pop_heap(front.begin(), front.end()); HeapNode hnode = front.back(); front.pop_back(); Node *node = hnode.node; if(Visited(node)) continue; if(Expand(hnode)) Visit(node); } Select(); draw_size = selected.size(); } void Extraction::Init() { //I want to add all coarsable nodes //and all refinable node (being careful about recursive dependencies) for(Node *node = root; node != sink; node++) { if(!Visited(node)) continue; if(node != root && CanCoarse(node)) back.push_back(HeapNode(node, GetNodeError(node))); for(Node::iterator n = node->out_begin; n != node->out_end; n++) { Node *child = n->node; if(Visited(child)) continue; if(node_errors[child - root] != -1) continue; //already visited float *error = GetNodeError(child); if(CanRefine(node)) // TODO? && error > target_error front.push_back(HeapNode(child, error)); if(*error > max_error) max_error = *error; } } //recursively fix error for(Node *node = root; node != sink; node++) if(node_errors[node - root] != -1) SetError(node, node_errors[node-root]); //estimate cost of all the cut arcs (i need the visible info) Cost cost; for(Node *node = root; node != sink; node++) { if(!Visited(node)) continue; for(Node::iterator n = node->out_begin; n != node->out_end; n++) { Link &link = *n; if(Visited((*n).node)) continue; for(unsigned int patch = link.begin; patch != link.end; patch++) { Entry &entry = (*mt)[patch]; cost.extr += entry.ram_size; if(Visible(patch)) cost.draw += entry.ram_size; if(!entry.patch) cost.disk += entry.disk_size; } } } make_heap(front.begin(), front.end()); make_heap(back.begin(), back.end(), greater()); extr_used = cost.extr; draw_used = cost.draw; disk_used = cost.disk; } void Extraction::Update(Nexus *_mt) { mt = _mt; root = mt->history.Root(); sink = mt->history.Sink(); if(!visited.size()) { visited.resize(mt->history.n_nodes(), false); SetVisited(root, true); } visible.clear(); visible.resize(mt->size(), true); node_errors.clear(); node_errors.resize(mt->history.n_nodes(), -1); front.clear(); back.clear(); max_error = -1; Init(); bool can_refine = true; while(1) { while(can_refine) { //we have available budget if(!front.size()) break; //we are at max level if(*front[0].error < target_error) break; //already at target_error max_error = *front[0].error; pop_heap(front.begin(), front.end()); HeapNode hnode = front.back(); front.pop_back(); if(!Visited(hnode.node) && CanRefine(hnode.node)) { if(!Refine(hnode)) { can_refine = false; front.push_back(hnode); push_heap(front.begin(), front.end()); } } } if(!back.size()) //nothing to coarse (happen only on extr_max < root.extr) break; if(*back.front().error >= target_error && (!front.size() || (*back.front().error * 1.4) >= *front.front().error)) break; pop_heap(back.begin(), back.end(), greater()); HeapNode hnode = back.back(); back.pop_back(); if(Visited(hnode.node) && //not already coarsed CanCoarse(hnode.node) && //all children !visited !Coarse(hnode)) { //no more disk back.push_back(hnode); push_heap(back.begin(), back.end(), greater()); break; } can_refine = true; } Select(); draw_size = selected.size(); //Preloading now for(unsigned int i = 0; i < 1000; i++) { if(!front.size() && !back.size()) break; if((i%2) && front.size()) { pop_heap(front.begin(), front.end()); HeapNode hnode = front.back(); Node *node = hnode.node; front.pop_back(); Node::iterator l; for(l = node->out_begin; l != node->out_end; l++) { Link &link = (*l); for(unsigned int k = link.begin; k != link.end; k++) { selected.push_back(Item(k, i)); } } } else if(back.size()) { pop_heap(back.begin(), back.end(), greater()); HeapNode hnode = back.back(); Node *node = hnode.node; back.pop_back(); for(Node::iterator l = node->in_begin; l != node->in_end; l++) { Link &link = (*l); for(unsigned int k = link.begin; k != link.end; k++) { selected.push_back(Item(k, i)); } } } } } float *Extraction::GetNodeError(Node *node) { float &maxerror = node_errors[node-root]; for(Node::iterator i = node->in_begin; i != node->in_end; i++) { Link &link = *i; for(unsigned int p = link.begin; p != link.end; p++) { Entry &entry = (*mt)[p]; bool visible; float error = metric->GetError(entry, visible); // cerr << "Error for patch: " << p << " -> " << error << endl; if(error > maxerror) maxerror = error; SetVisible(p, visible); } } return &maxerror; } bool Extraction::Refine(HeapNode hnode) { Node *node = hnode.node; Cost cost; Diff(node, cost); if(disk_used + cost.disk > disk_max || extr_used + cost.extr > extr_max || draw_used + cost.draw > draw_max) return false; extr_used += cost.extr; draw_used += cost.draw; disk_used += cost.disk; SetVisited(node, true); //now add to the front children (unless sink node) for(Node::iterator i = node->out_begin; i != node->out_end; i++) { Link &link = *i; if(link.node == sink) continue; float *error = &node_errors[link.node - root]; if(*error == -1) error = GetNodeError(link.node); if(*hnode.error < *error) *hnode.error = *error; //TODO if(maxerror > target_error) if(CanRefine((*i).node)) { front.push_back(HeapNode((*i).node, error)); push_heap(front.begin(), front.end()); } } back.push_back(hnode); push_heap(back.begin(), back.end(), greater()); return true; } bool Extraction::Coarse(HeapNode hnode) { Node *node = hnode.node; Cost cost; Diff(node, cost); extr_used -= cost.extr; draw_used -= cost.draw; disk_used -= cost.disk; if(disk_used > disk_max) return false; SetVisited(node, false); //now add to the back parents (unless root node) for(Node::iterator i = node->in_begin; i != node->in_end; i++) { Link &link = *i; if(link.node == root) continue; float *error = &node_errors[link.node - root]; if(*error == -1) error = GetNodeError(link.node); if(*error < *hnode.error) *error = *hnode.error; if(CanCoarse(link.node)) { back.push_back(HeapNode(link.node, error)); push_heap(back.begin(), back.end(), greater()); } } front.push_back(hnode); push_heap(front.begin(), front.end()); return true; } void Extraction::Select() { selected.clear(); Node *root = mt->history.Root(); Node *nodes = mt->history.nodes; for(unsigned int i = 0; i < visited.size(); i++) { if(!visited[i]) continue; Node &node = nodes[i]; Node::iterator n; for(n = node.out_begin; n != node.out_end; n++) { unsigned int n_out = (*n).node - root; if(!visited[n_out]) { Link &link = *n; for(unsigned int p = link.begin; p != link.end; p++) { selected.push_back(Item(p, 0)); } } } } } void Extraction::Visit(Node *node) { assert(!Visited(node)); SetVisited(node, true); for(Node::iterator i = node->in_begin; i != node->in_end; i++) { if(Visited((*i).node)) continue; Visit((*i).node); } Cost cost; Diff(node, cost); extr_used += cost.extr; draw_used += cost.draw; disk_used += cost.disk; for(Node::iterator i = node->out_begin; i != node->out_end; i++) { float maxerror = -1; Link &link = *i; for(unsigned int p = link.begin; p != link.end; p++) { Entry &entry = (*mt)[p]; bool visible; float error = metric->GetError(entry, visible); if(error > maxerror) maxerror = error; SetVisible(p, visible); } //TODO this check may be dangerous for non saturating things... if(maxerror > target_error) { node_errors[(*i).node - root] = maxerror; HeapNode hnode((*i).node, &node_errors[(*i).node - root]); front.push_back(hnode); push_heap(front.begin(), front.end()); } } } bool Extraction::Expand(HeapNode &node) { if(extr_used >= extr_max) return false; if(draw_used >= draw_max) return false; // if(disk_used >= disk_max) return false; return *node.error > target_error; } void Extraction::Diff(Node *node, Cost &cost) { Node::iterator i; for(i = node->in_begin; i != node->in_end; i++) { Link &link = *i; for(unsigned int p = link.begin; p != link.end; p++) { Entry &entry = (*mt)[p]; cost.extr -= entry.ram_size; if(Visible(p)) cost.draw -= entry.ram_size; if(!entry.patch) cost.disk -= entry.disk_size; } } for(i = node->out_begin; i != node->out_end; i++) { Link &link = *i; for(unsigned int p = link.begin; p != link.end; p++) { Entry &entry = (*mt)[p]; cost.extr += entry.ram_size; if(Visible(p)) cost.draw += entry.ram_size; if(!entry.patch) cost.disk += entry.disk_size; } } } void Extraction::SetError(Node *node, float error) { for(Node::iterator i = node->in_begin; i != node->in_end; i++) if(node_errors[(*i).node - root] != -1 && node_errors[(*i).node - root] < error) { node_errors[(*i).node - root] = error; SetError((*i).node, error); } } bool Extraction::CanRefine(Node *node) { for(Node::iterator i = node->in_begin; i != node->in_end; i++) if(!Visited((*i).node)) return false; return true; } bool Extraction::CanCoarse(Node *node) { for(Node::iterator i = node->out_begin; i != node->out_end; i++) if(Visited((*i).node)) return false; return true; }