Blender V2.61 - r43446

reorganize.h

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version. 
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2009 Blender Foundation.
00019  * All rights reserved.
00020  *
00021  * The Original Code is: all of this file.
00022  *
00023  * Contributor(s): André Pinto.
00024  *
00025  * ***** END GPL LICENSE BLOCK *****
00026  */
00027 
00033 #include <float.h>
00034 #include <math.h>
00035 #include <stdio.h>
00036 
00037 #include <algorithm>
00038 #include <queue>
00039 #include <vector>
00040 
00041 #include "BKE_global.h"
00042 
00043 #ifdef _WIN32
00044 #  ifdef INFINITY
00045 #    undef INFINITY
00046 #  endif
00047 #  define INFINITY FLT_MAX // in mingw math.h: (1.0F/0.0F). This generates compile error, though.
00048 #endif
00049 
00050 extern int tot_pushup;
00051 extern int tot_pushdown;
00052 
00053 #if !defined(INFINITY) && defined(HUGE_VAL)
00054 #define INFINITY HUGE_VAL
00055 #endif
00056 
00057 template<class Node>
00058 bool node_fits_inside(Node *a, Node *b)
00059 {
00060     return bb_fits_inside(b->bb, b->bb+3, a->bb, a->bb+3);
00061 }
00062 
00063 template<class Node>
00064 void reorganize_find_fittest_parent(Node *tree, Node *node, std::pair<float,Node*> &cost)
00065 {
00066     std::queue<Node*> q;
00067     q.push(tree);
00068     
00069     while(!q.empty())
00070     {
00071         Node *parent = q.front();
00072         q.pop();
00073         
00074         if(parent == node) continue;
00075         if(node_fits_inside(node, parent) && RE_rayobject_isAligned(parent->child) )
00076         {
00077             float pcost = bb_area(parent->bb, parent->bb+3);
00078             cost = std::min( cost, std::make_pair(pcost,parent) );
00079             for(Node *child = parent->child; child; child = child->sibling)
00080                 q.push(child);          
00081         }
00082     }
00083 }
00084 
00085 static int tot_moves = 0;
00086 template<class Node>
00087 void reorganize(Node *root)
00088 {
00089     std::queue<Node*> q;
00090 
00091     q.push(root);
00092     while(!q.empty())
00093     {
00094         Node * node = q.front();
00095         q.pop();
00096         
00097         if( RE_rayobject_isAligned(node->child) )
00098         {
00099             for(Node **prev = &node->child; *prev; )
00100             {
00101                 assert( RE_rayobject_isAligned(*prev) ); 
00102                 q.push(*prev);
00103 
00104                 std::pair<float,Node*> best(FLT_MAX, root);
00105                 reorganize_find_fittest_parent( root, *prev, best );
00106 
00107                 if(best.second == node)
00108                 {
00109                     //Already inside the fitnest BB
00110                     prev = &(*prev)->sibling;
00111                 }
00112                 else
00113                 {
00114                     Node *tmp = *prev;
00115                     *prev = (*prev)->sibling;
00116                     
00117                     tmp->sibling =  best.second->child;
00118                     best.second->child = tmp;
00119                     
00120                     tot_moves++;
00121                 }
00122             
00123             
00124             }
00125         }
00126         if(node != root)
00127         {
00128         }
00129     }
00130 }
00131 
00132 /*
00133  * Prunes useless nodes from trees:
00134  *  erases nodes with total amount of primitives = 0
00135  *  prunes nodes with only one child (except if that child is a primitive)
00136  */
00137 template<class Node>
00138 void remove_useless(Node *node, Node **new_node)
00139 {
00140     if( RE_rayobject_isAligned(node->child) )
00141     {
00142 
00143         for(Node **prev = &node->child; *prev; )
00144         {
00145             Node *next = (*prev)->sibling;
00146             remove_useless(*prev, prev);
00147             if(*prev == 0)
00148                 *prev = next;
00149             else
00150             {
00151                 (*prev)->sibling = next;
00152                 prev = &((*prev)->sibling);
00153             }
00154         }           
00155     }
00156     if(node->child)
00157     {
00158         if(RE_rayobject_isAligned(node->child) && node->child->sibling == 0)
00159             *new_node = node->child;
00160     }
00161     else if(node->child == 0)
00162         *new_node = 0;  
00163 }
00164 
00165 /*
00166  * Minimizes expected number of BBtest by colapsing nodes
00167  * it uses surface area heuristic for determining whether a node should be colapsed
00168  */
00169 template<class Node>
00170 void pushup(Node *parent)
00171 {
00172     if(is_leaf(parent)) return;
00173     
00174     float p_area = bb_area(parent->bb, parent->bb+3);
00175     Node **prev = &parent->child;
00176     for(Node *child = parent->child; RE_rayobject_isAligned(child) && child; )
00177     {
00178         float c_area = bb_area(child->bb, child->bb+3) ;
00179         int nchilds = count_childs(child);
00180         float original_cost = ((p_area != 0.0f)? (c_area / p_area)*nchilds: 1.0f) + 1;
00181         float flatten_cost = nchilds;
00182         if(flatten_cost < original_cost && nchilds >= 2)
00183         {
00184             append_sibling(child, child->child);
00185             child = child->sibling;
00186             *prev = child;
00187 
00188 //          *prev = child->child;
00189 //          append_sibling( *prev, child->sibling );
00190 //          child = *prev;
00191             tot_pushup++;
00192         }
00193         else
00194         {
00195             *prev = child;
00196             prev = &(*prev)->sibling;
00197             child = *prev;
00198         }       
00199     }
00200     
00201     for(Node *child = parent->child; RE_rayobject_isAligned(child) && child; child = child->sibling)
00202         pushup(child);
00203 }
00204 
00205 /*
00206  * try to optimize number of childs to be a multiple of SSize
00207  */
00208 template<class Node, int SSize>
00209 void pushup_simd(Node *parent)
00210 {
00211     if(is_leaf(parent)) return;
00212     
00213     int n = count_childs(parent);
00214         
00215     Node **prev = &parent->child;
00216     for(Node *child = parent->child; RE_rayobject_isAligned(child) && child; )
00217     {
00218         int cn = count_childs(child);
00219         if(cn-1 <= (SSize - (n%SSize) ) % SSize && RE_rayobject_isAligned(child->child) )
00220         {
00221             n += (cn - 1);
00222             append_sibling(child, child->child);
00223             child = child->sibling;
00224             *prev = child;  
00225         }
00226         else
00227         {
00228             *prev = child;
00229             prev = &(*prev)->sibling;
00230             child = *prev;
00231         }       
00232     }
00233         
00234     for(Node *child = parent->child; RE_rayobject_isAligned(child) && child; child = child->sibling)
00235         pushup_simd<Node,SSize>(child);
00236 }
00237 
00238 
00239 /*
00240  * Pushdown
00241  *  makes sure no child fits inside any of its sibling
00242  */
00243 template<class Node>
00244 void pushdown(Node *parent)
00245 {
00246     Node **s_child = &parent->child;
00247     Node * child = parent->child;
00248     
00249     while(child && RE_rayobject_isAligned(child))
00250     {
00251         Node *next = child->sibling;
00252         Node **next_s_child = &child->sibling;
00253         
00254         //assert(bb_fits_inside(parent->bb, parent->bb+3, child->bb, child->bb+3));
00255         
00256         for(Node *i = parent->child; RE_rayobject_isAligned(i) && i; i = i->sibling)
00257         if(child != i && bb_fits_inside(i->bb, i->bb+3, child->bb, child->bb+3) && RE_rayobject_isAligned(i->child))
00258         {
00259 //          todo optimize (should the one with the smallest area?)
00260 //          float ia = bb_area(i->bb, i->bb+3)
00261 //          if(child->i)
00262             *s_child = child->sibling;
00263             child->sibling = i->child;
00264             i->child = child;
00265             next_s_child = s_child;
00266             
00267             tot_pushdown++;
00268             break;
00269         }
00270         child = next;
00271         s_child = next_s_child;
00272     }
00273     
00274     for(Node *i = parent->child; RE_rayobject_isAligned(i) && i; i = i->sibling)
00275         pushdown( i );  
00276 }
00277 
00278 
00279 /*
00280  * BVH refit
00281  * readjust nodes BB (useful if nodes childs where modified)
00282  */
00283 template<class Node>
00284 float bvh_refit(Node *node)
00285 {
00286     if(is_leaf(node)) return 0; 
00287     if(is_leaf(node->child)) return 0;
00288     
00289     float total = 0;
00290     
00291     for(Node *child = node->child; child; child = child->sibling)
00292         total += bvh_refit(child);
00293         
00294     float old_area = bb_area(node->bb, node->bb+3);
00295     INIT_MINMAX(node->bb, node->bb+3);
00296     for(Node *child = node->child; child; child = child->sibling)
00297     {
00298         DO_MIN(child->bb, node->bb);
00299         DO_MAX(child->bb+3, node->bb+3);
00300     }
00301     total += old_area - bb_area(node->bb, node->bb+3);
00302     return total;
00303 }
00304 
00305 
00306 /*
00307  * this finds the best way to packing a tree according to a given test cost function
00308  * with the purpose to reduce the expected cost (eg.: number of BB tests).
00309  */
00310 #include <vector>
00311 #define MAX_CUT_SIZE        4               /* svbvh assumes max 4 children! */
00312 #define MAX_OPTIMIZE_CHILDS MAX_CUT_SIZE
00313 
00314 struct OVBVHNode
00315 {
00316     float   bb[6];
00317 
00318     OVBVHNode *child;
00319     OVBVHNode *sibling;
00320     
00321     /*
00322      * Returns min cost to represent the subtree starting at the given node,
00323      * allowing it to have a given cutsize
00324      */
00325     float cut_cost[MAX_CUT_SIZE];
00326     float get_cost(int cutsize)
00327     {
00328         return cut_cost[cutsize-1];
00329     }
00330     
00331     /*
00332      * This saves the cut size of this child, when parent is reaching
00333      * its minimum cut with the given cut size
00334      */
00335     int cut_size[MAX_CUT_SIZE];
00336     int get_cut_size(int parent_cut_size)
00337     {
00338         return cut_size[parent_cut_size-1];
00339     }
00340     
00341     /*
00342      * Reorganize the node based on calculated cut costs
00343      */  
00344     int best_cutsize;
00345     void set_cut(int cutsize, OVBVHNode ***cut)
00346     {
00347         if(cutsize == 1)
00348         {
00349             **cut = this;
00350              *cut = &(**cut)->sibling;
00351         }
00352         else
00353         {
00354             if(cutsize > MAX_CUT_SIZE)
00355             {
00356                 for(OVBVHNode *child = this->child; child && RE_rayobject_isAligned(child); child = child->sibling)
00357                 {
00358                     child->set_cut( 1, cut );
00359                     cutsize--;
00360                 }
00361                 assert(cutsize == 0);
00362             }
00363             else
00364                 for(OVBVHNode *child = this->child; child && RE_rayobject_isAligned(child); child = child->sibling)
00365                     child->set_cut( child->get_cut_size( cutsize ), cut );
00366         }
00367     }
00368 
00369     void optimize()
00370     {
00371         if(RE_rayobject_isAligned(this->child))
00372         {
00373             //Calc new childs
00374             {
00375                 OVBVHNode **cut = &(this->child);
00376                 set_cut( best_cutsize, &cut );
00377                 *cut = NULL;
00378             }
00379 
00380             //Optimize new childs
00381             for(OVBVHNode *child = this->child; child && RE_rayobject_isAligned(child); child = child->sibling)
00382                 child->optimize();
00383         }       
00384     }
00385 };
00386 
00387 /*
00388  * Calculates an optimal SIMD packing
00389  *
00390  */
00391 template<class Node, class TestCost>
00392 struct VBVH_optimalPackSIMD
00393 {
00394     TestCost testcost;
00395     
00396     VBVH_optimalPackSIMD(TestCost testcost)
00397     {
00398         this->testcost = testcost;
00399     }
00400     
00401     /*
00402      * calc best cut on a node
00403      */
00404     struct calc_best
00405     {
00406         Node *child[MAX_OPTIMIZE_CHILDS];
00407         float child_hit_prob[MAX_OPTIMIZE_CHILDS];
00408         
00409         calc_best(Node *node)
00410         {
00411             int nchilds = 0;
00412             //Fetch childs and needed data
00413             {
00414                 float parent_area = bb_area(node->bb, node->bb+3);
00415                 for(Node *child = node->child; child && RE_rayobject_isAligned(child); child = child->sibling)
00416                 {
00417                     this->child[nchilds] = child;
00418                     this->child_hit_prob[nchilds] = (parent_area != 0.0f)? bb_area(child->bb, child->bb+3) / parent_area: 1.0f;
00419                     nchilds++;
00420                 }
00421 
00422                 assert(nchilds >= 2 && nchilds <= MAX_OPTIMIZE_CHILDS);
00423             }
00424             
00425             
00426             //Build DP table to find minimum cost to represent this node with a given cutsize
00427             int   bt  [MAX_OPTIMIZE_CHILDS+1][MAX_CUT_SIZE+1]; //backtrace table
00428             float cost[MAX_OPTIMIZE_CHILDS+1][MAX_CUT_SIZE+1]; //cost table (can be reduced to float[2][MAX_CUT_COST])
00429             
00430             for(int i=0; i<=nchilds; i++)
00431             for(int j=0; j<=MAX_CUT_SIZE; j++)
00432                 cost[i][j] = INFINITY;
00433 
00434             cost[0][0] = 0;
00435             
00436             for(int i=1; i<=nchilds; i++)
00437             for(int size=i-1; size/*+(nchilds-i)*/<=MAX_CUT_SIZE; size++)
00438             for(int cut=1; cut+size/*+(nchilds-i)*/<=MAX_CUT_SIZE; cut++)
00439             {
00440                 float new_cost = cost[i-1][size] + child_hit_prob[i-1]*child[i-1]->get_cost(cut);
00441                 if(new_cost < cost[i][size+cut])
00442                 {
00443                     cost[i][size+cut] = new_cost;
00444                     bt[i][size+cut] = cut;
00445                 }
00446             }
00447             
00448             //Save the ways to archieve the minimum cost with a given cutsize
00449             for(int i = nchilds; i <= MAX_CUT_SIZE; i++)
00450             {
00451                 node->cut_cost[i-1] = cost[nchilds][i];
00452                 if(cost[nchilds][i] < INFINITY)
00453                 {
00454                     int current_size = i;
00455                     for(int j=nchilds; j>0; j--)
00456                     {
00457                         child[j-1]->cut_size[i-1] = bt[j][current_size];
00458                         current_size -= bt[j][current_size];
00459                     }
00460                 }
00461             }           
00462         }
00463     };
00464     
00465     void calc_costs(Node *node)
00466     {
00467         
00468         if( RE_rayobject_isAligned(node->child) )
00469         {
00470             int nchilds = 0;
00471             for(Node *child = node->child; child && RE_rayobject_isAligned(child); child = child->sibling)
00472             {
00473                 calc_costs(child);
00474                 nchilds++;
00475             }
00476 
00477             for(int i=0; i<MAX_CUT_SIZE; i++)
00478                 node->cut_cost[i] = INFINITY;
00479 
00480             //We are not allowed to look on nodes with with so many childs
00481             if(nchilds > MAX_CUT_SIZE)
00482             {
00483                 float cost = 0;
00484 
00485                 float parent_area = bb_area(node->bb, node->bb+3);
00486                 for(Node *child = node->child; child && RE_rayobject_isAligned(child); child = child->sibling)
00487                 {
00488                     cost += ((parent_area != 0.0f)? ( bb_area(child->bb, child->bb+3) / parent_area ): 1.0f) * child->get_cost(1);
00489                 }
00490                 
00491                 cost += testcost(nchilds);
00492                 node->cut_cost[0] = cost;
00493                 node->best_cutsize = nchilds;
00494             }
00495             else
00496             {
00497                 calc_best calc(node);
00498         
00499                 //calc expected cost if we optimaly pack this node
00500                 for(int cutsize=nchilds; cutsize<=MAX_CUT_SIZE; cutsize++)
00501                 {
00502                     float m = node->get_cost(cutsize) + testcost(cutsize);
00503                     if(m < node->cut_cost[0])
00504                     {
00505                         node->cut_cost[0] = m;
00506                         node->best_cutsize = cutsize;
00507                     }
00508                 }
00509             }
00510             assert(node->cut_cost[0] != INFINITY);
00511         }
00512         else
00513         {
00514             node->cut_cost[0] = 1.0f;
00515             for(int i=1; i<MAX_CUT_SIZE; i++)
00516                 node->cut_cost[i] = INFINITY;
00517         }
00518     }
00519 
00520     Node *transform(Node *node)
00521     {
00522         if(RE_rayobject_isAligned(node->child))
00523         {
00524             static int num = 0;
00525             bool first = false;
00526             if(num == 0) { num++; first = true; }
00527             
00528             calc_costs(node);
00529             if((G.f & G_DEBUG) && first) printf("expected cost = %f (%d)\n", node->cut_cost[0], node->best_cutsize );
00530             node->optimize();
00531         }
00532         return node;        
00533     }   
00534 };