1  /********************************************************************** <BR>


2  This file is part of Crack dot Com's free source code release of


3  Golgotha. <a href="http://www.crack.com/golgotha_release"> <BR> for


4  information about compiling & licensing issues visit this URL</a>


5  <PRE> If that doesn't help, contact Jonathan Clark at


6  golgotha_source@usa.net (Subject should have "GOLG" in it)


7  ***********************************************************************/


8 


9  #include "tex_heap.hh"


10  #include "tex_no_heap.hh"


11 


12  i4_profile_class pf_tex_heap_alloc("tex_heap::alloc()");


13  i4_profile_class pf_tex_heap_free("tex_heap::free()");


14  i4_profile_class pf_tex_heap_cleanup("tex_heap::cleanup()");


15 


16  i4_profile_class pf_tex_no_heap_alloc("tex_heap::alloc()");


17  i4_profile_class pf_tex_no_heap_free("tex_heap::free()");


18  i4_profile_class pf_tex_no_heap_cleanup("tex_heap::cleanup()");


19 


20 


21  r1_texture_heap_class::~r1_texture_heap_class()


22  {


23  //kill the free list


24  r1_tex_heap_free_node *next_free,*last_free;


25 


26  next_free = first_free;


27  while (next_free)


28  {


29  last_free = next_free;


30  next_free = next_free>next;


31 


32  if (last_free>mip)


33  {


34  last_free>mip>vram_handle = 0;


35  }


36  free_node_alloc>free(last_free);


37  }


38  first_free = 0;


39 


40  //kill the used list


41  r1_tex_heap_used_node *next_used,*last_used;


42 


43  next_used = first_used;


44  while (next_used)


45  {


46  last_used = next_used;


47  next_used = next_used>next;


48 


49  if (last_used>node>mip>vram_handle)


50  {


51  last_used>node>mip>vram_handle = 0;


52  }


53  free_node_alloc>free(last_used>node);


54  used_node_alloc>free(last_used);


55  }


56  first_used = 0;


57 


58  oldest_used = 0;


59  //both lists are cleared. kill the allocaters


60 


61  delete free_node_alloc;


62  free_node_alloc=0;


63 


64  delete used_node_alloc;


65  used_node_alloc=0;


66  }


67 


68  r1_texture_heap_class::r1_texture_heap_class(w32 heap_size, w32 heap_start, w32 free_node_size, w32 used_node_size, sw32 *frame_count)


69  {


70  num_ram_misses = 0;


71  needs_cleanup = i4_F;


72 


73  free_node_alloc = 0;


74  used_node_alloc = 0;


75 


76  frame_count_ptr = frame_count;


77 


78  if (!free_node_alloc)


79  {


80  free_node_alloc = new i4_linear_allocator(free_node_size, 2048, 1024, "texture heap free nodes");


81  first_free = (r1_tex_heap_free_node *)free_node_alloc>alloc();


82  first_free>size = heap_size;


83  first_free>start = heap_start;


84  first_free>next = 0;


85  first_free>mip = 0;


86  }


87 


88  if (!used_node_alloc)


89  {


90  used_node_alloc = new i4_linear_allocator(used_node_size, 2048, 1024, "texture heap used nodes");


91  first_used = 0;


92  oldest_used = 0;


93  }


94  }


95 


96 


97  void r1_texture_heap_class::free_really_old()


98  {


99  pf_tex_heap_cleanup.start();


100 


101  w32 total_freed = 0;


102 


103  r1_tex_heap_used_node *u = oldest_used;


104  r1_tex_heap_used_node *last;


105 


106  while (u && total_freed < max_fail_size)


107  {


108  sw32 lfu = u>node>mip>last_frame_used;


109  sw32 age = *frame_count_ptr  lfu;


110 


111  last = u>last;


112 


113  if ((lfu != 1) && (age > 10))


114  {


115  total_freed += u>node>size;


116 


117  free(u);


118  }


119 


120  u = last;


121  }


122 


123  // if (total_freed < max_fail_size)


124  // i4_warning("Texture cleanup: freed %d bytes (not enough)",total_freed);


125  // else


126  // i4_warning("Texture cleanup: freed %d bytes (successful)",total_freed);


127 


128  max_fail_size = 0;


129  needs_cleanup = i4_F;


130 


131  pf_tex_heap_cleanup.stop();


132  }


133 


134 


135 


136  r1_tex_heap_used_node *r1_texture_heap_class::alloc(w32 need_size, w8 flags)


137  {


138  pf_tex_heap_alloc.start();


139 


140  //ok. we know how much memory we need, now try to find a free area of memory


141  r1_tex_heap_free_node *f = first_free;


142  r1_tex_heap_free_node *last = 0;


143 


144  while (f && f>size<need_size)


145  {


146  last=f;


147  f=f>next;


148  }


149 


150  r1_tex_heap_used_node *new_used = 0;


151 


152  if (!f)


153  {


154  //no memory large enough. dig through and see if there is a used chunk that we can free


155  r1_tex_heap_used_node *u = oldest_used;


156 


157  while (u)


158  {


159  if (u>node>size >= need_size)


160  {


161  r1_miplevel_t *m = u>node>mip;


162  if (m>last_frame_used!=1 && m>last_frame_used < (*frame_count_ptr  2))


163  {


164  if (m>flags & R1_MIPLEVEL_IS_LOADING)


165  {


166  i4_warning("loading miplevel is in used list. bad.");


167  }


168  break;


169  }


170  }


171  u = u>last;


172  }


173 


174  //couldnt find a used one that we could free


175  if (!u)


176  {


177  num_ram_misses++;


178 


179  if (need_size > max_fail_size)


180  max_fail_size = need_size;


181 


182  if ((num_ram_misses & 15) == 0)


183  {


184  //cleanup old textures every 16 misses


185  needs_cleanup = i4_T;


186  }


187 


188  pf_tex_heap_alloc.stop();


189  return 0;


190  }


191 


192  new_used = u;


193 


194  f = new_used>node;


195 


196  //kill the old mip's reference to it


197  f>mip>vram_handle = 0;


198 


199  if (f>size > need_size)


200  {


201  //need to merge unused space back into free list


202 


203  merge_into_free_list(f>start + need_size, f>size  need_size);


204 


205  f>size = need_size;


206  }


207 


208  if (flags & R1_TEX_HEAP_DONT_LIST)


209  {


210  if (new_used==first_used)


211  first_used = new_used>next;


212  if (new_used==oldest_used)


213  oldest_used = new_used>last;


214 


215  //this flag is set for asynchronously loaded textures


216  //we dont even want this node to be listed. it will be added


217  //when the async load is finished


218  if (new_used>next) new_used>next>last = new_used>last;


219  if (new_used>last) new_used>last>next = new_used>next;


220 


221  new_used>next=0;


222  new_used>last=0;


223  }


224  }


225  else


226  {


227  // remove the this node from the free list


228  if (!last)


229  first_free = f>next;


230  else


231  last>next = f>next;


232 


233  //if we dont match the size exactly, need to create a new free node to fill in the space


234  if (f>size != need_size)


235  {


236  r1_tex_heap_free_node *new_free = (r1_tex_heap_free_node *)free_node_alloc>alloc();


237 


238  if (!new_free)


239  {


240  i4_warning("alloc of tex_heap free_node failed");


241  pf_tex_heap_alloc.stop();


242  return 0;


243  }


244 


245  new_free>size = f>size  need_size;


246  new_free>start = f>start + need_size;


247  new_free>mip=0;


248 


249  if (last)


250  last>next = new_free;


251  else


252  first_free = new_free;


253 


254 


255  new_free>next = f>next;


256  f>size = need_size;


257  }


258 


259  new_used = (r1_tex_heap_used_node *)used_node_alloc>alloc();


260 


261  //f is the node we'll use


262  //add into the used_list


263 


264  if (!new_used)


265  {


266  i4_warning("alloc of tex_heap used_node failed");


267  pf_tex_heap_alloc.stop();


268  return 0;


269  }


270 


271  new_used>node = f;


272 


273  if (flags & R1_TEX_HEAP_DONT_LIST)


274  {


275  new_used>next=0;


276  new_used>last=0;


277  }


278  else


279  {


280  new_used>next = first_used;


281  new_used>last = 0;


282 


283  if (first_used)


284  first_used>last = new_used;


285 


286  first_used = new_used;


287  }


288  }


289 


290  pf_tex_heap_alloc.stop();


291  return new_used;


292  }


293 


294  void r1_texture_heap_class::merge_into_free_list(w32 _start, w32 _size)


295  {


296  r1_tex_heap_free_node *next, *last;


297 


298  // find proper spot to add into free list


299  next = first_free; last=0;


300  for (; next && next>start < _start;)


301  {


302  last = next;


303  next = next>next;


304  }


305 


306  // can we combine with the last node?


307  if (last && last>start+last>size == _start)


308  {


309  last>size += _size;


310 


311  // does this also combine with the next node?


312  if (next && (last>start + last>size == next>start) )


313  {


314  last>size += next>size;


315  last>next = next>next;


316 


317  free_node_alloc>free(next);


318  }


319  }


320  else


321  if (next && (_start + _size == next>start) ) // does it combine with the next block?


322  {


323  next>start = _start;


324  next>size += _size;


325  }


326  else


327  if (last) // put after the last proper node


328  {


329  r1_tex_heap_free_node *f = (r1_tex_heap_free_node *)free_node_alloc>alloc();


330 


331  f>size = _size;


332  f>start = _start;


333  f>next = last>next; //(last>next should be NULL)


334  f>mip = 0;


335 


336  last>next = f;


337  }


338  else // put it at the begining of the list


339  {


340  r1_tex_heap_free_node *f = (r1_tex_heap_free_node *)free_node_alloc>alloc();


341 


342  f>size = _size;


343  f>start = _start;


344  f>next = first_free;


345  f>mip = 0;


346  first_free = f;


347  }


348  }


349 


350  void r1_texture_heap_class::update_usage(r1_tex_heap_used_node *u)


351  {


352  if (u>node>mip>last_frame_used==1)


353  return ;


354 


355 


356  u>node>mip>last_frame_used = *frame_count_ptr;


357 


358  if (u != first_used)


359  {


360  //put this node at the front of the used list


361  //has the effect of sorting all used nodes according to


362  //their age (most recently used are at the


363  //front of the list)


364 


365  if (u==oldest_used)


366  oldest_used = u>last;


367 


368  if (u>next) u>next>last = u>last;


369  if (u>last) u>last>next = u>next;


370 


371  u>last = 0;


372  u>next = first_used;


373 


374  if (first_used)


375  first_used>last = u;


376 


377  first_used = u;


378  }


379 


380  //update the oldest used pointer


381  if (!oldest_used)


382  oldest_used = u;


383  else


384  {


385  if ((u>node>mip>last_frame_used != 1) &&


386  (u>node>mip>last_frame_used < oldest_used>node>mip>last_frame_used))


387  {


388  oldest_used = u;


389  }


390  }


391  }


392 


393  void r1_texture_heap_class::free(r1_tex_heap_used_node *u)


394  {


395  if (!u  !u>node)


396  {


397  i4_warning("tex_heap_class free : handle is 0");


398  return;


399  }


400 


401  if (u>node>mip>flags & R1_MIPLEVEL_IS_LOADING)


402  {


403  i4_warning("free called on a mip that was still loading");


404  }


405 


406  pf_tex_heap_free.start();


407 


408  r1_tex_heap_free_node *f = u>node, *next, *last;


409 


410  //give its r1_miplevel_t an invalid vram handle


411  f>mip>vram_handle = 0;


412 


413  //process these cases 1st


414  if (u==first_used)


415  first_used = u>next;


416 


417  if (u==oldest_used)


418  oldest_used = u>last;


419 


420  //pull it from the used_list


421  if (u>next) u>next>last = u>last;


422  if (u>last) u>last>next = u>next;


423 


424  used_node_alloc>free(u);


425  //end of pulling from the used list


426 


427  // find proper spot to add into free list


428  next = first_free; last=0;


429  for (; next && next>start<f>start;)


430  {


431  last=next;


432  next=next>next;


433  }


434 


435  // can we combine with the last node?


436  if (last && last>start+last>size==f>start)


437  {


438  last>size += f>size;


439 


440  // does this also combine with the next node?


441  if (next && (last>start + last>size == next>start) )


442  {


443  last>size += next>size;


444  last>next = next>next;


445 


446  free_node_alloc>free(next);


447  }


448 


449  free_node_alloc>free(f);


450  }


451  else


452  if (next && (f>start + f>size == next>start) ) // does it combine with the next block?


453  {


454  next>start = f>start;


455  next>size += f>size;


456 


457  free_node_alloc>free(f);


458  }


459  else


460  if (last) // put after the last proper node


461  {


462  f>next = last>next;


463  last>next = f;


464  }


465  else // put it at the begining of the list


466  {


467  f>next = first_free;


468  first_free = f;


469  }


470 


471  pf_tex_heap_free.stop();


472  }

