Author Topic: Performance Improvements (Updated 22/06/14)  (Read 10924 times)

0 Members and 1 Guest are viewing this topic.

Offline Kobrar44

  • On Suspended Sentence
  • 29
  • Let me tilerape it for you!
    • Steam
Re: Performance Improvements (Updated 16/03/13)
When would these optimizations give the potential performance boost? I guess the best way is to fred a mission with a whole lotta of this specific stuff in a controlled way.
Oh guys, use that [ url ][ img ][ /img ][ /url ] :/

 
Re: Performance Improvements (Updated 16/03/13)
The biggest effect should be in the loading of textures, especially when rendering animations (loads *every* frame to be rendered).

I would go for something involving lots of ships jumping in and getting destroyed, as well as lots of beamfire. The sort of thing that tends to kill framerate anyway.

Such as massive battle...

 

Offline Goober5000

  • HLP Loremaster
  • Moderator
  • 214
    • Goober5000 Productions
Re: Performance Improvements (Updated 16/03/13)
:bump: for progress.  Also for news on the new EFF format. :nervous:

 

Offline mjn.mixael

  • Cutscene Master
  • 212
  • Chopped liver
    • Steam
    • Twitter
Re: Performance Improvements (Updated 16/03/13)
As much as I want the new EFF format.. isn't 3.7 final far more important?
Cutscene Upgrade Project - Mainhall Remakes - Between the Ashes
Youtube Channel - P3D Model Box
Between the Ashes is looking for committed testers, PM me for details.
Freespace Upgrade Project See what's happening.

 

Offline Goober5000

  • HLP Loremaster
  • Moderator
  • 214
    • Goober5000 Productions
Re: Performance Improvements (Updated 16/03/13)
Yes indeed.  But Flaming_Sword is a bit of a unique case.  He hasn't been closely involved in SCP development for a few years now, but he is just about the only person who has made serious progress on the new EFF format (or any similar format).  So I asked him back just for that.  I'm concerned he may have dropped off the grid again. :(

 
Re: Performance Improvements (Updated 16/03/13)
Sorry for the absence, between a blown up machine and work (and Guild Wars 2 :nervous:), I haven't been looking at FS2 for a while.

That said, the performance patch was attached in this thread and can be merged wherever whenever. EFF has that spec document lying around, not sure if there are still copies online somewhere. I recall finding an actual TLV library on sourceforge or something, but it didn't seem like it suited our needs here.

I believe any EFF code I've written so far is in the "WTF was I thinking when I wrote that?" stage and will likely get thrown out. Hopefully, it's all still sitting on a hard drive somewhere in the house...

If anything the first stage of EFF would be a separate generic library for reading/writing so it might be a fair while before any FS2 code rears its head.

 

Offline Goober5000

  • HLP Loremaster
  • Moderator
  • 214
    • Goober5000 Productions
Re: Performance Improvements (Updated 16/03/13)
Hmm.  If you mean the patch in the first post, it got deleted in a recent attachment purge.  Could you re-upload it?

 
Re: Performance Improvements (Updated 16/03/13)
I'll dig around for it. I'm also pretty sure this APNG thing might work out better than what I had for EFF, relegating the ideas to simply a potential replacement for VPs.

 
Re: Performance Improvements (Updated 16/03/13)
Pretty sure this one is the patch.

[attachment deleted by ninja]

 
Re: Performance Improvements (Updated 16/03/13)
Now I feel stupid. The following patch is probably a much better way to do it. Please test.

Code: [Select]
Index: code/model/modelcollide.cpp
===================================================================
--- code/model/modelcollide.cpp (revision 9771)
+++ code/model/modelcollide.cpp (working copy)
@@ -76,7 +76,7 @@
 // Returns non-zero if vector from p0 to pdir
 // intersects the bounding box.
 // hitpos could be NULL, so don't fill it if it is.
-int mc_ray_boundingbox( vec3d *min, vec3d *max, vec3d * p0, vec3d *pdir, vec3d *hitpos )
+inline int mc_ray_boundingbox( vec3d *min, vec3d *max, vec3d * p0, vec3d *pdir, vec3d *hitpos )
 {
 
  vec3d tmp_hitpos;
@@ -481,7 +481,7 @@
 
 int model_collide_sub( void *model_ptr );
 
-void model_collide_sortnorm(ubyte * p)
+inline void model_collide_sortnorm(ubyte * p)
 {
  int frontlist = w(p+36);
  int backlist = w(p+40);
@@ -509,7 +509,7 @@
 
 //calls the object interpreter to render an object.  The object renderer
 //is really a seperate pipeline. returns true if drew
-int model_collide_sub(void *model_ptr )
+inline int model_collide_sub(void *model_ptr )
 {
  ubyte *p = (ubyte *)model_ptr;
  int chunk_type, chunk_size;
@@ -602,7 +602,7 @@
  }
 }
 
-void model_collide_bsp(bsp_collision_tree *tree, int node_index)
+inline void model_collide_bsp(bsp_collision_tree *tree, int node_index)
 {
  if ( tree->node_list == NULL || tree->n_verts <= 0) {
  return;
@@ -949,7 +949,7 @@
  return false;
 }
 
-bool mc_check_sldc(int offset)
+inline bool mc_check_sldc(int offset)
 {
  if (offset > Mc_pm->sldc_size-5) //no way is this big enough
  return false;
@@ -999,7 +999,7 @@
 }
 
 // checks a vector collision against a ships shield (if it has shield points defined).
-void mc_check_shield()
+inline void mc_check_shield()
 {
  int i;
 
@@ -1031,7 +1031,7 @@
 
 // This function recursively checks a submodel and its children
 // for a collision with a vector.
-void mc_check_subobj( int mn )
+inline void mc_check_subobj( int mn )
 {
  vec3d tempv;
  vec3d hitpt; // used in bounding box check
Index: code/math/fvi.cpp
===================================================================
--- code/math/fvi.cpp (revision 9771)
+++ code/math/fvi.cpp (working copy)
@@ -314,75 +314,75 @@
  return 0;
 }
 
-/**
- * Finds intersection of a ray and an axis-aligned bounding box
- *
- * Given a ray with origin at p0, and direction pdir, this function
- * returns non-zero if that ray intersects an axis-aligned bounding box
- * from min to max.   If there was an intersection, then hitpt will contain
- * the point where the ray begins inside the box.
- * Fast ray-box intersection taken from Graphics Gems I, pages 395,736.
- */
-int fvi_ray_boundingbox( vec3d *min, vec3d *max, vec3d * p0, vec3d *pdir, vec3d *hitpt )
-{
- bool inside = true;
- bool middle[3] = { true, true, true };
- int i;
- int which_plane;
- float maxt[3];
- float candidate_plane[3];
+///**
+// * Finds intersection of a ray and an axis-aligned bounding box
+// *
+// * Given a ray with origin at p0, and direction pdir, this function
+// * returns non-zero if that ray intersects an axis-aligned bounding box
+// * from min to max.   If there was an intersection, then hitpt will contain
+// * the point where the ray begins inside the box.
+// * Fast ray-box intersection taken from Graphics Gems I, pages 395,736.
+// */
+//int fvi_ray_boundingbox( vec3d *min, vec3d *max, vec3d * p0, vec3d *pdir, vec3d *hitpt )
+//{
+// bool inside = true;
+// bool middle[3] = { true, true, true };
+// int i;
+// int which_plane;
+// float maxt[3];
+// float candidate_plane[3];
+//
+// for (i = 0; i < 3; i++) {
+// if (p0->a1d[i] < min->a1d[i]) {
+// candidate_plane[i] = min->a1d[i];
+// middle[i] = false;
+// inside = false;
+// } else if (p0->a1d[i] > max->a1d[i]) {
+// candidate_plane[i] = max->a1d[i];
+// middle[i] = false;
+// inside = false;
+// }
+// }
+//
+// // ray origin inside bounding box
+// if ( inside ) {
+// *hitpt = *p0;
+// return 1;
+// }
+//
+// // calculate T distances to candidate plane
+// for (i = 0; i < 3; i++) {
+// if ( !middle[i] && (pdir->a1d[i] != 0.0f) )
+// maxt[i] = (candidate_plane[i] - p0->a1d[i]) / pdir->a1d[i];
+// else
+// maxt[i] = -1.0f;
+// }
+//
+// // Get largest of the maxt's for final choice of intersection
+// which_plane = 0;
+// for (i = 1; i < 3; i++) {
+// if (maxt[which_plane] < maxt[i])
+// which_plane = i;
+// }
+//
+// // check final candidate actually inside box
+// if (maxt[which_plane] < 0.0f)
+// return 0;
+//
+// for (i = 0; i < 3; i++) {
+// if (which_plane != i) {
+// hitpt->a1d[i] = p0->a1d[i] + maxt[which_plane] * pdir->a1d[i];
+//
+// if ( (hitpt->a1d[i] < min->a1d[i]) || (hitpt->a1d[i] > max->a1d[i]) )
+// return 0;
+// } else {
+// hitpt->a1d[i] = candidate_plane[i];
+// }
+// }
+//
+// return 1;
+//}
 
- for (i = 0; i < 3; i++) {
- if (p0->a1d[i] < min->a1d[i]) {
- candidate_plane[i] = min->a1d[i];
- middle[i] = false;
- inside = false;
- } else if (p0->a1d[i] > max->a1d[i]) {
- candidate_plane[i] = max->a1d[i];
- middle[i] = false;
- inside = false;
- }
- }
-
- // ray origin inside bounding box
- if ( inside ) {
- *hitpt = *p0;
- return 1;
- }
-
- // calculate T distances to candidate plane
- for (i = 0; i < 3; i++) {
- if ( !middle[i] && (pdir->a1d[i] != 0.0f) )
- maxt[i] = (candidate_plane[i] - p0->a1d[i]) / pdir->a1d[i];
- else
- maxt[i] = -1.0f;
- }
-
- // Get largest of the maxt's for final choice of intersection
- which_plane = 0;
- for (i = 1; i < 3; i++) {
- if (maxt[which_plane] < maxt[i])
- which_plane = i;
- }
-
- // check final candidate actually inside box
- if (maxt[which_plane] < 0.0f)
- return 0;
-
- for (i = 0; i < 3; i++) {
- if (which_plane != i) {
- hitpt->a1d[i] = p0->a1d[i] + maxt[which_plane] * pdir->a1d[i];
-
- if ( (hitpt->a1d[i] < min->a1d[i]) || (hitpt->a1d[i] > max->a1d[i]) )
- return 0;
- } else {
- hitpt->a1d[i] = candidate_plane[i];
- }
- }
-
- return 1;
-}
-
 /**
  * Given largest componant of normal, return i & j
  * If largest componant is negative, swap i & j
Index: code/math/fvi.h
===================================================================
--- code/math/fvi.h (revision 9771)
+++ code/math/fvi.h (working copy)
@@ -104,8 +104,77 @@
 // from min to max.   If there was an intersection, then hitpt will contain
 // the point where the ray begins inside the box.
 // Fast ray-box intersection taken from Graphics Gems I, pages 395,736.
-int fvi_ray_boundingbox( vec3d *min, vec3d *max, vec3d * p0, vec3d *pdir, vec3d *hitpt );
+//int fvi_ray_boundingbox( vec3d *min, vec3d *max, vec3d * p0, vec3d *pdir, vec3d *hitpt );
 
+/**
+ * Finds intersection of a ray and an axis-aligned bounding box
+ *
+ * Given a ray with origin at p0, and direction pdir, this function
+ * returns non-zero if that ray intersects an axis-aligned bounding box
+ * from min to max.   If there was an intersection, then hitpt will contain
+ * the point where the ray begins inside the box.
+ * Fast ray-box intersection taken from Graphics Gems I, pages 395,736.
+ */
+inline int fvi_ray_boundingbox( vec3d *min, vec3d *max, vec3d * p0, vec3d *pdir, vec3d *hitpt )
+{
+  bool inside = true;
+  bool middle[3] = { true, true, true };
+  int i;
+  int which_plane;
+  float maxt[3];
+  float candidate_plane[3];
+
+  for (i = 0; i < 3; i++) {
+    if (p0->a1d[i] < min->a1d[i]) {
+      candidate_plane[i] = min->a1d[i];
+      middle[i] = false;
+      inside = false;
+    } else if (p0->a1d[i] > max->a1d[i]) {
+      candidate_plane[i] = max->a1d[i];
+      middle[i] = false;
+      inside = false;
+    }
+  }
+
+  // ray origin inside bounding box
+  if ( inside ) {
+    *hitpt = *p0;
+    return 1;
+  }
+
+  // calculate T distances to candidate plane
+  for (i = 0; i < 3; i++) {
+    if ( !middle[i] && (pdir->a1d[i] != 0.0f) )
+      maxt[i] = (candidate_plane[i] - p0->a1d[i]) / pdir->a1d[i];
+    else
+      maxt[i] = -1.0f;
+  }
+
+  // Get largest of the maxt's for final choice of intersection
+  which_plane = 0;
+  for (i = 1; i < 3; i++) {
+    if (maxt[which_plane] < maxt[i])
+      which_plane = i;
+  }
+
+  // check final candidate actually inside box
+  if (maxt[which_plane] < 0.0f)
+    return 0;
+
+  for (i = 0; i < 3; i++) {
+    if (which_plane != i) {
+      hitpt->a1d[i] = p0->a1d[i] + maxt[which_plane] * pdir->a1d[i];
+
+      if ( (hitpt->a1d[i] < min->a1d[i]) || (hitpt->a1d[i] > max->a1d[i]) )
+        return 0;
+    } else {
+      hitpt->a1d[i] = candidate_plane[i];
+    }
+  }
+
+  return 1;
+}
+
 // sphere polygon collision prototypes
 
 // Given a polygon vertex list and a moving sphere, find the first contact the sphere makes with the edge, if any

 

Offline Goober5000

  • HLP Loremaster
  • Moderator
  • 214
    • Goober5000 Productions
Re: Performance Improvements (Updated 16/03/13)
Maybe you could post a build with that patch applied? :nervous:

 
Re: Performance Improvements (Updated 08/09/13)
 :bump:

New patch and linux build posted. I don't have access to a Windows build environment at the moment.

 

Offline The E

  • He's Ebeneezer Goode
  • Moderator
  • 213
  • Nothing personal, just tech support.
    • Steam
    • Twitter
Re: Performance Improvements (Updated 08/09/13)
I've taken the liberty of uploading Windows builds myself and editing the post.
If I'm just aching this can't go on
I came from chasing dreams to feel alone
There must be changes, miss to feel strong
I really need lifе to touch me
--Evergrey, Where August Mourns

 

Offline chief1983

  • Still lacks a custom title
  • Moderator
  • 212
  • ⬇️⬆️⬅️⬅️🅰➡️⬇️
    • Minecraft
    • Skype
    • Steam
    • Twitter
    • Fate of the Galaxy
Re: Performance Improvements (Updated 08/09/13)
I'll try to get a Mac one up if no one else does.
Fate of the Galaxy - Now Hiring!  Apply within | Diaspora | SCP Home | Collada Importer for PCS2
Karajorma's 'How to report bugs' | Mantis
#freespace | #scp-swc | #diaspora | #SCP | #hard-light on EsperNet

"You may not sell or otherwise commercially exploit the source or things you created based on the source." -- Excerpt from FSO license, for reference

Nuclear1:  Jesus Christ zack you're a little too hamyurger for HLP right now...
iamzack:  i dont have hamynerge i just want ptatoc hips D:
redsniper:  Platonic hips?!
iamzack:  lays

 
Re: Performance Improvements (Updated 21/09/13)
 :bump:

New patch posted, with tweak for shaders. Also included, linux debug builds print stacktrace debug upon int3(); (run "./autogen.sh --enable-debug" again)

Hopefully, this plays nice with the builds posted here:

http://www.hard-light.net/forums/index.php?topic=85618.0

 

Offline chief1983

  • Still lacks a custom title
  • Moderator
  • 212
  • ⬇️⬆️⬅️⬅️🅰➡️⬇️
    • Minecraft
    • Skype
    • Steam
    • Twitter
    • Fate of the Galaxy
Re: Performance Improvements (Updated 21/09/13)
Mac build link added to first post.
Fate of the Galaxy - Now Hiring!  Apply within | Diaspora | SCP Home | Collada Importer for PCS2
Karajorma's 'How to report bugs' | Mantis
#freespace | #scp-swc | #diaspora | #SCP | #hard-light on EsperNet

"You may not sell or otherwise commercially exploit the source or things you created based on the source." -- Excerpt from FSO license, for reference

Nuclear1:  Jesus Christ zack you're a little too hamyurger for HLP right now...
iamzack:  i dont have hamynerge i just want ptatoc hips D:
redsniper:  Platonic hips?!
iamzack:  lays

 

Offline niffiwan

  • 211
  • Eluder Class
Re: Performance Improvements (Updated 21/09/13)
Also included, linux debug builds print stacktrace debug upon int3(); (run "./autogen.sh --enable-debug" again)

That's very cool :)  Since we tend to be removing int3()'s from the code, could this be applied to Assert/Assertion as well?

And... :nervous: is it feasible to implement a "pause to attach debugger" style dialogue likes windows builds have? There's been a number of occasions when I really wished that I could do that (usually when trying to track down hard to reproduce bugs).
Creating a fs2_open.log | Red Alert Bug = Hex Edit | MediaVPs 2014: Bigger HUD gauges | 32bit libs for 64bit Ubuntu
----
Debian Packages (testing/unstable): Freespace2 | wxLauncher
----
m|m: I think I'm suffering from Stockholm syndrome. Bmpman is starting to make sense and it's actually written reasonably well...

 
Re: Performance Improvements (Updated 21/09/13)
The code can simply be moved to where it's needed.

Also, I'm pretty sure you can use gdb since debug versions are compiled with -g.

 
Re: Performance Improvements (Updated 25/01/14)
:bump:

Updated patch for trunk 10338, posted below and attached.

Code: [Select]
Index: code/bmpman/bm_internal.h
===================================================================
--- code/bmpman/bm_internal.h (revision 10338)
+++ code/bmpman/bm_internal.h (working copy)
@@ -21,6 +21,12 @@
 #include "globalincs/pstypes.h"
 #include "bmpman/bmpman.h"
 
+#include "ddsutils/ddsutils.h"
+#include "pcxutils/pcxutils.h"
+#include "pngutils/pngutils.h"
+#include "jpgutils/jpgutils.h"
+#include "tgautils/tgautils.h"
+#include "palman/palman.h"
 
 // no-type ( used in: bm_bitmaps[i].type )
 #define BM_TYPE_NONE 0
@@ -114,18 +120,444 @@
 } bitmap_entry;
 
 extern bitmap_entry bm_bitmaps[MAX_BITMAPS];
+extern int Is_standalone;
+//
+//// image specific lock functions
+//void bm_lock_ani( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags );
+//void bm_lock_dds( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags );
+//void bm_lock_png( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags );
+//void bm_lock_jpg( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags );
+//void bm_lock_pcx( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags );
+//void bm_lock_tga( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags );
+//void bm_lock_user( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags );
+#define EFF_FILENAME_CHECK { if ( be->type == BM_TYPE_EFF ) strncpy( filename, be->info.ani.eff.filename, MAX_FILENAME_LEN ); else strncpy( filename, be->filename, MAX_FILENAME_LEN ); }
 
+inline void bm_lock_pcx( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags )
+{
+  ubyte *data;
+  int pcx_error;
+  char filename[MAX_FILENAME_LEN];
 
-// image specific lock functions
-void bm_lock_ani( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags );
-void bm_lock_dds( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags );
-void bm_lock_png( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags );
-void bm_lock_jpg( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags );
-void bm_lock_pcx( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags );
-void bm_lock_tga( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags );
-void bm_lock_user( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags );
+  // Unload any existing data
+  bm_free_data( bitmapnum, false );
 
+  be->mem_taken = (bmp->w * bmp->h * (bpp >> 3));
+  data = (ubyte *)bm_malloc(bitmapnum, be->mem_taken);
+  bmp->bpp = bpp;
+  bmp->data = (ptr_u)data;
+  bmp->palette = (bpp == 8) ? gr_palette : NULL;
+  memset( data, 0, be->mem_taken );
 
+  Assert( &be->bm == bmp );
+#ifdef BMPMAN_NDEBUG
+  Assert( be->data_size > 0 );
+#endif
+
+  // some sanity checks on flags
+  Assert(!((flags & BMP_AABITMAP) && (flags & BMP_TEX_ANY)));           // no aabitmap textures
+
+  // make sure we are using the correct filename in the case of an EFF.
+  // this will populate filename[] whether it's EFF or not
+  EFF_FILENAME_CHECK;
+
+  pcx_error = pcx_read_bitmap( filename, data, NULL, (bpp >> 3), (flags & BMP_AABITMAP), 0, be->dir_type );
+
+  if ( pcx_error != PCX_ERROR_NONE ) {
+    mprintf(("Couldn't load PCX!!! (%s)\n", filename));
+    return;
+  }
+
+#ifdef BMPMAN_NDEBUG
+  Assert( be->data_size > 0 );
+#endif
+
+  bmp->flags = 0;
+
+  bm_convert_format( bitmapnum, bmp, bpp, flags );
+}
+
+inline void bm_lock_ani( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags )
+{
+  anim        *the_anim;
+  anim_instance *the_anim_instance;
+  bitmap      *bm;
+  ubyte       *frame_data;
+  int       size, i;
+  int       first_frame, nframes;
+
+  first_frame = be->info.ani.first_frame;
+  nframes = bm_bitmaps[first_frame].info.ani.num_frames;
+
+  if ( (the_anim = anim_load(bm_bitmaps[first_frame].filename, bm_bitmaps[first_frame].dir_type)) == NULL ) {
+    nprintf(("BMPMAN", "Error opening %s in bm_lock\n", be->filename));
+    return;
+  }
+
+  if ( (the_anim_instance = init_anim_instance(the_anim, bpp)) == NULL ) {
+    nprintf(("BMPMAN", "Error opening %s in bm_lock\n", be->filename));
+    anim_free(the_anim);
+    return;
+  }
+
+  int can_drop_frames = 0;
+
+  if ( the_anim->total_frames != bm_bitmaps[first_frame].info.ani.num_frames )  {
+    can_drop_frames = 1;
+  }
+
+  bm = &bm_bitmaps[first_frame].bm;
+  size = bm->w * bm->h * (bpp >> 3);
+  be->mem_taken = size;
+
+  Assert( size > 0 );
+
+  for ( i=0; i<nframes; i++ ) {
+    be = &bm_bitmaps[first_frame+i];
+    bm = &bm_bitmaps[first_frame+i].bm;
+
+    // Unload any existing data
+    bm_free_data( first_frame+i, false );
+
+    bm->flags = 0;
+
+    // briefing editor in Fred2 uses aabitmaps (ani's) - force to 8 bit
+    bm->bpp = Is_standalone ? (ubyte)8 : bpp;
+
+    bm->data = (ptr_u)bm_malloc(first_frame + i, size);
+
+    frame_data = anim_get_next_raw_buffer(the_anim_instance, 0 ,flags & BMP_AABITMAP ? 1 : 0, bm->bpp);
+
+    ubyte *dptr, *sptr;
+
+    sptr = frame_data;
+    dptr = (ubyte *)bm->data;
+
+    if ( (bm->w!=the_anim->width) || (bm->h!=the_anim->height) )  {
+      // Scale it down
+      // 8 bit
+      if(bpp == 8){
+        int w,h;
+        fix u, utmp, v, du, dv;
+
+        u = v = 0;
+
+        du = ( the_anim->width*F1_0 ) / bm->w;
+        dv = ( the_anim->height*F1_0 ) / bm->h;
+
+        for (h = 0; h < bm->h; h++) {
+          ubyte *drow = &dptr[bm->w * h];
+          ubyte *srow = &sptr[f2i(v)*the_anim->width];
+
+          utmp = u;
+
+          for (w = 0; w < bm->w; w++) {
+            *drow++ = srow[f2i(utmp)];
+            utmp += du;
+          }
+          v += dv;
+        }
+      }
+      // 16 bpp
+      else {
+        int w,h;
+        fix u, utmp, v, du, dv;
+
+        u = v = 0;
+
+        du = ( the_anim->width*F1_0 ) / bm->w;
+        dv = ( the_anim->height*F1_0 ) / bm->h;
+
+        for (h = 0; h < bm->h; h++) {
+          unsigned short *drow = &((unsigned short*)dptr)[bm->w * h];
+          unsigned short *srow = &((unsigned short*)sptr)[f2i(v)*the_anim->width];
+
+          utmp = u;
+
+          for (w = 0; w < bm->w; w++) {
+            *drow++ = srow[f2i(utmp)];
+            utmp += du;
+          }
+          v += dv;
+        }
+      }
+    } else {
+      // 1-to-1 mapping
+      memcpy(dptr, sptr, size);
+    }
+
+    bm_convert_format( first_frame+i, bm, bpp, flags );
+
+    // Skip a frame
+    if ( (i < nframes-1)  && can_drop_frames )  {
+      frame_data = anim_get_next_raw_buffer(the_anim_instance, 0, flags & BMP_AABITMAP ? 1 : 0, bm->bpp);
+    }
+  }
+
+  free_anim_instance(the_anim_instance);
+  anim_free(the_anim);
+}
+
+
+inline void bm_lock_user( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags )
+{
+  // Unload any existing data
+  bm_free_data( bitmapnum, false );
+
+  if ((bpp != be->info.user.bpp) && !(flags & BMP_AABITMAP))
+    bpp = be->info.user.bpp;
+
+  switch ( bpp ) {
+    case 32:  // user 32-bit bitmap
+      bmp->bpp = bpp;
+      bmp->flags = be->info.user.flags;
+      bmp->data = (ptr_u)be->info.user.data;
+      break;
+
+    case 24:  // user 24-bit bitmap
+      bmp->bpp = bpp;
+      bmp->flags = be->info.user.flags;
+      bmp->data = (ptr_u)be->info.user.data;
+      break;
+
+    case 16:      // user 16 bit bitmap
+      bmp->bpp = bpp;
+      bmp->flags = be->info.user.flags;
+      bmp->data = (ptr_u)be->info.user.data;
+      break;
+
+    case 8:     // Going from 8 bpp to something (probably only for aabitmaps)
+      Assert(flags & BMP_AABITMAP);
+      bmp->bpp = bpp;
+      bmp->flags = be->info.user.flags;
+      bmp->data = (ptr_u)be->info.user.data;
+      break;
+
+     default:
+      Error( LOCATION, "Unhandled user bitmap conversion from %d to %d bpp", be->info.user.bpp, bmp->bpp );
+      break;
+  }
+
+  bm_convert_format( bitmapnum, bmp, bpp, flags );
+}
+
+inline void bm_lock_tga( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags )
+{
+  ubyte *data = NULL;
+  int d_size, byte_size;
+  char filename[MAX_FILENAME_LEN];
+
+  // Unload any existing data
+  bm_free_data( bitmapnum, false );
+
+  if(Is_standalone){
+    Assert(bpp == 8);
+  }
+  else
+  {
+    Assert( (bpp == 16) || (bpp == 24 ) || (bpp == 32) );
+  }
+
+  // allocate bitmap data
+  byte_size = (bpp >> 3);
+
+  Assert( byte_size );
+  Assert( be->mem_taken > 0 );
+
+  data = (ubyte*)bm_malloc(bitmapnum, be->mem_taken);
+
+  if (data) {
+    memset( data, 0, be->mem_taken);
+    d_size = byte_size;
+  } else {
+    return;
+  }
+
+  bmp->bpp = bpp;
+  bmp->data = (ptr_u)data;
+  bmp->palette = NULL;
+
+  Assert( &be->bm == bmp );
+#ifdef BMPMAN_NDEBUG
+  Assert( be->data_size > 0 );
+#endif
+
+  int tga_error;
+
+  // make sure we are using the correct filename in the case of an EFF.
+  // this will populate filename[] whether it's EFF or not
+  EFF_FILENAME_CHECK;
+
+  tga_error = targa_read_bitmap( filename, data, NULL, d_size, be->dir_type);
+
+  if ( tga_error != TARGA_ERROR_NONE )  {
+    bm_free_data( bitmapnum, false );
+    return;
+  }
+
+  bmp->flags = 0;
+
+  bm_convert_format( bitmapnum, bmp, bpp, flags );
+}
+
+/**
+ * Lock a DDS file
+ */
+inline void bm_lock_dds( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags )
+{
+  ubyte *data = NULL;
+  int error;
+  ubyte dds_bpp = 0;
+  char filename[MAX_FILENAME_LEN];
+
+  // free any existing data
+  bm_free_data( bitmapnum, false );
+
+  Assert( be->mem_taken > 0 );
+  Assert( &be->bm == bmp );
+
+  data = (ubyte*)bm_malloc(bitmapnum, be->mem_taken);
+
+  if ( data == NULL )
+    return;
+
+  memset( data, 0, be->mem_taken );
+
+  // make sure we are using the correct filename in the case of an EFF.
+  // this will populate filename[] whether it's EFF or not
+  EFF_FILENAME_CHECK;
+
+  error = dds_read_bitmap( filename, data, &dds_bpp, be->dir_type );
+
+#if BYTE_ORDER == BIG_ENDIAN
+  // same as with TGA, we need to byte swap 16 & 32-bit, uncompressed, DDS images
+  if ( (be->comp_type == BM_TYPE_DDS) || (be->comp_type == BM_TYPE_CUBEMAP_DDS) ) {
+    unsigned int i = 0;
+
+    if (dds_bpp == 32) {
+      unsigned int *swap_tmp;
+
+      for (i = 0; i < (unsigned int)be->mem_taken; i += 4) {
+        swap_tmp = (unsigned int *)(data + i);
+        *swap_tmp = INTEL_INT(*swap_tmp);
+      }
+    } else if (dds_bpp == 16) {
+      unsigned short *swap_tmp;
+
+      for (i = 0; i < (unsigned int)be->mem_taken; i += 2) {
+        swap_tmp = (unsigned short *)(data + i);
+        *swap_tmp = INTEL_SHORT(*swap_tmp);
+      }
+    }
+  }
+#endif
+
+  bmp->bpp = dds_bpp;
+  bmp->data = (ptr_u)data;
+  bmp->flags = 0;
+
+  if (error != DDS_ERROR_NONE) {
+    bm_free_data( bitmapnum, false );
+    return;
+  }
+
+#ifdef BMPMAN_NDEBUG
+  Assert( be->data_size > 0 );
+#endif
+}
+
+/**
+ * Lock a PNG file
+ */
+inline void bm_lock_png( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags )
+{
+  ubyte *data = NULL;
+  //assume 32 bit - libpng should expand everything
+  int d_size;
+  int png_error = PNG_ERROR_INVALID;
+  char filename[MAX_FILENAME_LEN];
+
+  // Unload any existing data
+  bm_free_data( bitmapnum, false );
+
+  // allocate bitmap data
+  Assert( bmp->w * bmp->h > 0 );
+
+  //if it's not 32-bit, we expand when we read it
+  bmp->bpp = 32;
+  d_size = bmp->bpp >> 3;
+  //we waste memory if it turns out to be 24-bit, but the way this whole thing works is dodgy anyway
+  data = (ubyte*)bm_malloc(bitmapnum, bmp->w * bmp->h * d_size);
+  if (data == NULL)
+    return;
+  memset( data, 0, bmp->w * bmp->h * d_size);
+  bmp->data = (ptr_u)data;
+  bmp->palette = NULL;
+
+  Assert( &be->bm == bmp );
+
+  // make sure we are using the correct filename in the case of an EFF.
+  // this will populate filename[] whether it's EFF or not
+  EFF_FILENAME_CHECK;
+
+  //bmp->bpp gets set correctly in here after reading into memory
+  png_error = png_read_bitmap( filename, data, &bmp->bpp, d_size, be->dir_type );
+
+  if ( png_error != PNG_ERROR_NONE )  {
+    bm_free_data( bitmapnum, false );
+    return;
+  }
+
+#ifdef BMPMAN_NDEBUG
+  Assert( be->data_size > 0 );
+#endif
+}
+
+/**
+ * Lock a JPEG file
+ */
+inline void bm_lock_jpg( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags )
+{
+  ubyte *data = NULL;
+  int d_size = 0;
+  int jpg_error = JPEG_ERROR_INVALID;
+  char filename[MAX_FILENAME_LEN];
+
+  // Unload any existing data
+  bm_free_data( bitmapnum, false );
+
+  d_size = (bpp >> 3);
+
+  // allocate bitmap data
+  Assert( be->mem_taken > 0 );
+  data = (ubyte*)bm_malloc(bitmapnum, be->mem_taken);
+
+  if (data == NULL)
+    return;
+
+  memset( data, 0, be->mem_taken);
+
+  bmp->bpp = bpp;
+  bmp->data = (ptr_u)data;
+  bmp->palette = NULL;
+
+  Assert( &be->bm == bmp );
+
+  // make sure we are using the correct filename in the case of an EFF.
+  // this will populate filename[] whether it's EFF or not
+  EFF_FILENAME_CHECK;
+
+  jpg_error = jpeg_read_bitmap( filename, data, NULL, d_size, be->dir_type );
+
+  if ( jpg_error != JPEG_ERROR_NONE ) {
+    bm_free_data( bitmapnum, false );
+    return;
+  }
+
+#ifdef BMPMAN_NDEBUG
+  Assert( be->data_size > 0 );
+#endif
+}
+
 #endif // BMPMAN_INTERNAL
 
 #endif // __BM_INTERNAL_H__
Index: code/bmpman/bmpman.cpp
===================================================================
--- code/bmpman/bmpman.cpp (revision 10338)
+++ code/bmpman/bmpman.cpp (working copy)
@@ -109,7 +109,7 @@
 /**
  * Frees a bitmaps data if it can
  */
-static void bm_free_data(int n, bool release = false)
+void bm_free_data(int n, bool release = false)
 {
  bitmap_entry *be;
  bitmap *bmp;
@@ -225,7 +225,7 @@
 
 void bm_clean_slot(int n)
 {
- bm_free_data(n);
+ bm_free_data(n, false);
 }
 
 
@@ -265,7 +265,7 @@
  int i;
  if ( bm_inited ) {
  for (i=0; i<MAX_BITMAPS; i++ ) {
- bm_free_data(i); // clears flags, bbp, data, etc
+ bm_free_data(i, false); // clears flags, bbp, data, etc
  }
  bm_inited = 0;
  }
@@ -304,7 +304,7 @@
 
  gr_bm_init(i);
 
- bm_free_data(i);  // clears flags, bbp, data, etc
+ bm_free_data(i, false);  // clears flags, bbp, data, etc
  }
 }
 
@@ -1191,7 +1191,7 @@
  return bm_bitmaps[bitmapnum].type;
 }
 
-static void bm_convert_format( int bitmapnum, bitmap *bmp, ubyte bpp, ubyte flags )
+void bm_convert_format( int bitmapnum, bitmap *bmp, ubyte bpp, ubyte flags )
 {
  int idx;
 
@@ -1211,7 +1211,7 @@
 
  // maybe swizzle to be an xparent texture
  if(!(bmp->flags & BMP_TEX_XPARENT) && (flags & BMP_TEX_XPARENT)){
- for(idx=0; idx<bmp->w*bmp->h; idx++){
+ for(idx=0; idx<bmp->w*bmp->h; idx++){
  // if the pixel is transparent
  if ( ((ushort*)bmp->data)[idx] == Gr_t_green.mask) {
  ((ushort*)bmp->data)[idx] = 0;
@@ -1219,9 +1219,12 @@
  }
 
  bmp->flags |= BMP_TEX_XPARENT;
- }
+ }
 }
 
+MONITOR(NumBitmapPage)
+MONITOR(SizeBitmapPage)
+
 // basically, map the bitmap into the current palette. used to be done for all pcx's, now just for
 // Fred, since its the only thing that uses the software tmapper
 void bm_swizzle_8bit_for_fred(bitmap_entry *be, bitmap *bmp, ubyte *data, ubyte *palette)
@@ -1229,436 +1232,7 @@
 /* 2004/10/17 - taylor - no longer needed since FRED is OGL now*/
 }
 
-void bm_lock_pcx( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags )
-{
- ubyte *data;
- int pcx_error;
- char filename[MAX_FILENAME_LEN];
-
- // Unload any existing data
- bm_free_data( bitmapnum );
-
- be->mem_taken = (bmp->w * bmp->h * (bpp >> 3));
- data = (ubyte *)bm_malloc(bitmapnum, be->mem_taken);
- bmp->bpp = bpp;
- bmp->data = (ptr_u)data;
- bmp->palette = (bpp == 8) ? gr_palette : NULL;
- memset( data, 0, be->mem_taken );
-
- Assert( &be->bm == bmp );
-#ifdef BMPMAN_NDEBUG
- Assert( be->data_size > 0 );
-#endif
-
- // some sanity checks on flags
- Assert(!((flags & BMP_AABITMAP) && (flags & BMP_TEX_ANY))); // no aabitmap textures
-
- // make sure we are using the correct filename in the case of an EFF.
- // this will populate filename[] whether it's EFF or not
- EFF_FILENAME_CHECK;
-
- pcx_error = pcx_read_bitmap( filename, data, NULL, (bpp >> 3), (flags & BMP_AABITMAP), 0, be->dir_type );
-
- if ( pcx_error != PCX_ERROR_NONE ) {
- mprintf(("Couldn't load PCX!!! (%s)\n", filename));
- return;
- }
-
-#ifdef BMPMAN_NDEBUG
- Assert( be->data_size > 0 );
-#endif
-
- bmp->flags = 0;
-
- bm_convert_format( bitmapnum, bmp, bpp, flags );
-}
-
-void bm_lock_ani( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags )
-{
- anim *the_anim;
- anim_instance *the_anim_instance;
- bitmap *bm;
- ubyte *frame_data;
- int size, i;
- int first_frame, nframes;
-
- first_frame = be->info.ani.first_frame;
- nframes = bm_bitmaps[first_frame].info.ani.num_frames;
-
- if ( (the_anim = anim_load(bm_bitmaps[first_frame].filename, bm_bitmaps[first_frame].dir_type)) == NULL ) {
- nprintf(("BMPMAN", "Error opening %s in bm_lock\n", be->filename));
- return;
- }
-
- if ( (the_anim_instance = init_anim_instance(the_anim, bpp)) == NULL ) {
- nprintf(("BMPMAN", "Error opening %s in bm_lock\n", be->filename));
- anim_free(the_anim);
- return;
- }
-
- int can_drop_frames = 0;
-
- if ( the_anim->total_frames != bm_bitmaps[first_frame].info.ani.num_frames ) {
- can_drop_frames = 1;
- }
-
- bm = &bm_bitmaps[first_frame].bm;
- size = bm->w * bm->h * (bpp >> 3);
- be->mem_taken = size;
-
- Assert( size > 0 );
-
- for ( i=0; i<nframes; i++ ) {
- be = &bm_bitmaps[first_frame+i];
- bm = &bm_bitmaps[first_frame+i].bm;
-
- // Unload any existing data
- bm_free_data( first_frame+i );
-
- bm->flags = 0;
-
- // briefing editor in Fred2 uses aabitmaps (ani's) - force to 8 bit
- bm->bpp = Is_standalone ? (ubyte)8 : bpp;
-
- bm->data = (ptr_u)bm_malloc(first_frame + i, size);
-
- frame_data = anim_get_next_raw_buffer(the_anim_instance, 0 ,flags & BMP_AABITMAP ? 1 : 0, bm->bpp);
-
- ubyte *dptr, *sptr;
-
- sptr = frame_data;
- dptr = (ubyte *)bm->data;
-
- if ( (bm->w!=the_anim->width) || (bm->h!=the_anim->height) ) {
- // Scale it down
- // 8 bit
- if(bpp == 8){
- int w,h;
- fix u, utmp, v, du, dv;
-
- u = v = 0;
-
- du = ( the_anim->width*F1_0 ) / bm->w;
- dv = ( the_anim->height*F1_0 ) / bm->h;
-
- for (h = 0; h < bm->h; h++) {
- ubyte *drow = &dptr[bm->w * h];
- ubyte *srow = &sptr[f2i(v)*the_anim->width];
-
- utmp = u;
-
- for (w = 0; w < bm->w; w++) {
- *drow++ = srow[f2i(utmp)];
- utmp += du;
- }
- v += dv;
- }
- }
- // 16 bpp
- else {
- int w,h;
- fix u, utmp, v, du, dv;
-
- u = v = 0;
-
- du = ( the_anim->width*F1_0 ) / bm->w;
- dv = ( the_anim->height*F1_0 ) / bm->h;
-
- for (h = 0; h < bm->h; h++) {
- unsigned short *drow = &((unsigned short*)dptr)[bm->w * h];
- unsigned short *srow = &((unsigned short*)sptr)[f2i(v)*the_anim->width];
-
- utmp = u;
-
- for (w = 0; w < bm->w; w++) {
- *drow++ = srow[f2i(utmp)];
- utmp += du;
- }
- v += dv;
- }
- }
- } else {
- // 1-to-1 mapping
- memcpy(dptr, sptr, size);
- }
-
- bm_convert_format( first_frame+i, bm, bpp, flags );
-
- // Skip a frame
- if ( (i < nframes-1)  && can_drop_frames ) {
- frame_data = anim_get_next_raw_buffer(the_anim_instance, 0, flags & BMP_AABITMAP ? 1 : 0, bm->bpp);
- }
- }
-
- free_anim_instance(the_anim_instance);
- anim_free(the_anim);
-}
-
-
-void bm_lock_user( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags )
-{
- // Unload any existing data
- bm_free_data( bitmapnum );
-
- if ((bpp != be->info.user.bpp) && !(flags & BMP_AABITMAP))
- bpp = be->info.user.bpp;
-
- switch ( bpp ) {
- case 32: // user 32-bit bitmap
- bmp->bpp = bpp;
- bmp->flags = be->info.user.flags;
- bmp->data = (ptr_u)be->info.user.data;
- break;
-
- case 24: // user 24-bit bitmap
- bmp->bpp = bpp;
- bmp->flags = be->info.user.flags;
- bmp->data = (ptr_u)be->info.user.data;
- break;
-
- case 16: // user 16 bit bitmap
- bmp->bpp = bpp;
- bmp->flags = be->info.user.flags;
- bmp->data = (ptr_u)be->info.user.data;
- break;
-
- case 8: // Going from 8 bpp to something (probably only for aabitmaps)
- Assert(flags & BMP_AABITMAP);
- bmp->bpp = bpp;
- bmp->flags = be->info.user.flags;
- bmp->data = (ptr_u)be->info.user.data;
- break;
-
- default:
- Error( LOCATION, "Unhandled user bitmap conversion from %d to %d bpp", be->info.user.bpp, bmp->bpp );
- break;
- }
-
- bm_convert_format( bitmapnum, bmp, bpp, flags );
-}
-
-void bm_lock_tga( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags )
-{
- ubyte *data = NULL;
- int d_size, byte_size;
- char filename[MAX_FILENAME_LEN];
-
- // Unload any existing data
- bm_free_data( bitmapnum );
-
- if(Is_standalone){
- Assert(bpp == 8);
- }
- else
- {
- Assert( (bpp == 16) || (bpp == 24 ) || (bpp == 32) );
- }
-
- // allocate bitmap data
- byte_size = (bpp >> 3);
-
- Assert( byte_size );
- Assert( be->mem_taken > 0 );
-
- data = (ubyte*)bm_malloc(bitmapnum, be->mem_taken);
-
- if (data) {
- memset( data, 0, be->mem_taken);
- d_size = byte_size;
- } else {
- return;
- }
-
- bmp->bpp = bpp;
- bmp->data = (ptr_u)data;
- bmp->palette = NULL;
-
- Assert( &be->bm == bmp );
-#ifdef BMPMAN_NDEBUG
- Assert( be->data_size > 0 );
-#endif
-
- int tga_error;
-
- // make sure we are using the correct filename in the case of an EFF.
- // this will populate filename[] whether it's EFF or not
- EFF_FILENAME_CHECK;
-
- tga_error = targa_read_bitmap( filename, data, NULL, d_size, be->dir_type);
-
- if ( tga_error != TARGA_ERROR_NONE ) {
- bm_free_data( bitmapnum );
- return;
- }
-
- bmp->flags = 0;
-
- bm_convert_format( bitmapnum, bmp, bpp, flags );
-}
-
 /**
- * Lock a DDS file
- */
-void bm_lock_dds( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags )
-{
- ubyte *data = NULL;
- int error;
- ubyte dds_bpp = 0;
- char filename[MAX_FILENAME_LEN];
-
- // free any existing data
- bm_free_data( bitmapnum );
-
- Assert( be->mem_taken > 0 );
- Assert( &be->bm == bmp );
-
- data = (ubyte*)bm_malloc(bitmapnum, be->mem_taken);
-
- if ( data == NULL )
- return;
-
- memset( data, 0, be->mem_taken );
-
- // make sure we are using the correct filename in the case of an EFF.
- // this will populate filename[] whether it's EFF or not
- EFF_FILENAME_CHECK;
-
- error = dds_read_bitmap( filename, data, &dds_bpp, be->dir_type );
-
-#if BYTE_ORDER == BIG_ENDIAN
- // same as with TGA, we need to byte swap 16 & 32-bit, uncompressed, DDS images
- if ( (be->comp_type == BM_TYPE_DDS) || (be->comp_type == BM_TYPE_CUBEMAP_DDS) ) {
- unsigned int i = 0;
-
- if (dds_bpp == 32) {
- unsigned int *swap_tmp;
-
- for (i = 0; i < (unsigned int)be->mem_taken; i += 4) {
- swap_tmp = (unsigned int *)(data + i);
- *swap_tmp = INTEL_INT(*swap_tmp);
- }
- } else if (dds_bpp == 16) {
- unsigned short *swap_tmp;
-
- for (i = 0; i < (unsigned int)be->mem_taken; i += 2) {
- swap_tmp = (unsigned short *)(data + i);
- *swap_tmp = INTEL_SHORT(*swap_tmp);
- }
- }
- }
-#endif
-
- bmp->bpp = dds_bpp;
- bmp->data = (ptr_u)data;
- bmp->flags = 0;
-
- if (error != DDS_ERROR_NONE) {
- bm_free_data( bitmapnum );
- return;
- }
-
-#ifdef BMPMAN_NDEBUG
- Assert( be->data_size > 0 );
-#endif
-}
-
-/**
- * Lock a PNG file
- */
-void bm_lock_png( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags )
-{
- ubyte *data = NULL;
- //assume 32 bit - libpng should expand everything
- int d_size;
- int png_error = PNG_ERROR_INVALID;
- char filename[MAX_FILENAME_LEN];
-
- // Unload any existing data
- bm_free_data( bitmapnum );
-
- // allocate bitmap data
- Assert( bmp->w * bmp->h > 0 );
-
- //if it's not 32-bit, we expand when we read it
- bmp->bpp = 32;
- d_size = bmp->bpp >> 3;
- //we waste memory if it turns out to be 24-bit, but the way this whole thing works is dodgy anyway
- data = (ubyte*)bm_malloc(bitmapnum, bmp->w * bmp->h * d_size);
- if (data == NULL)
- return;
- memset( data, 0, bmp->w * bmp->h * d_size);
- bmp->data = (ptr_u)data;
- bmp->palette = NULL;
-
- Assert( &be->bm == bmp );
-
- // make sure we are using the correct filename in the case of an EFF.
- // this will populate filename[] whether it's EFF or not
- EFF_FILENAME_CHECK;
-
- //bmp->bpp gets set correctly in here after reading into memory
- png_error = png_read_bitmap( filename, data, &bmp->bpp, d_size, be->dir_type );
-
- if ( png_error != PNG_ERROR_NONE ) {
- bm_free_data( bitmapnum );
- return;
- }
-
-#ifdef BMPMAN_NDEBUG
- Assert( be->data_size > 0 );
-#endif
-}
-
-/**
- * Lock a JPEG file
- */
-void bm_lock_jpg( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags )
-{
- ubyte *data = NULL;
- int d_size = 0;
- int jpg_error = JPEG_ERROR_INVALID;
- char filename[MAX_FILENAME_LEN];
-
- // Unload any existing data
- bm_free_data( bitmapnum );
-
- d_size = (bpp >> 3);
-
- // allocate bitmap data
- Assert( be->mem_taken > 0 );
- data = (ubyte*)bm_malloc(bitmapnum, be->mem_taken);
-
- if (data == NULL)
- return;
-
- memset( data, 0, be->mem_taken);
-
- bmp->bpp = bpp;
- bmp->data = (ptr_u)data;
- bmp->palette = NULL;
-
- Assert( &be->bm == bmp );
-
- // make sure we are using the correct filename in the case of an EFF.
- // this will populate filename[] whether it's EFF or not
- EFF_FILENAME_CHECK;
-
- jpg_error = jpeg_read_bitmap( filename, data, NULL, d_size, be->dir_type );
-
- if ( jpg_error != JPEG_ERROR_NONE ) {
- bm_free_data( bitmapnum );
- return;
- }
-
-#ifdef BMPMAN_NDEBUG
- Assert( be->data_size > 0 );
-#endif
-}
-
-MONITOR( NumBitmapPage )
-MONITOR( SizeBitmapPage )
-
-/**
  * This locks down a bitmap and returns a pointer to a bitmap that can be accessed until you call bm_unlock.
  *
  * Only lock a bitmap when you need it! This will convert it into the appropriate format also.
@@ -1995,7 +1569,7 @@
  } else {
  if(!nodebug)
  nprintf(("BmpMan", "Unloading %s.  %dx%dx%d\n", be->filename, bmp->w, bmp->h, bmp->bpp));
- bm_free_data(n); // clears flags, bbp, data, etc
+ bm_free_data(n, false); // clears flags, bbp, data, etc
  }
 
  return 1;
@@ -2075,7 +1649,7 @@
  int i;
  for (i = 0; i < MAX_BITMAPS; i++) {
  if ( bm_bitmaps[i].type != BM_TYPE_NONE ) {
- bm_free_data(i);
+ bm_free_data(i, false);
  }
  }
  dc_printf( "Total RAM after flush: %d bytes\n", bm_texture_ram );
Index: code/bmpman/bmpman.h
===================================================================
--- code/bmpman/bmpman.h (revision 10338)
+++ code/bmpman/bmpman.h (working copy)
@@ -285,4 +285,7 @@
 int bm_set_render_target(int handle, int face = -1);
 
 int bm_load_and_parse_eff(const char *filename, int dir_type, int *nframes, int *nfps, int *key, ubyte *type);
+
+void bm_free_data(int n, bool release);
+void bm_convert_format( int bitmapnum, bitmap *bmp, ubyte bpp, ubyte flags );
 #endif
Index: code/graphics/gropenglpostprocessing.cpp
===================================================================
--- code/graphics/gropenglpostprocessing.cpp (revision 10338)
+++ code/graphics/gropenglpostprocessing.cpp (working copy)
@@ -338,6 +338,8 @@
  GLboolean light = GL_state.Lighting(GL_FALSE);
  GLboolean blend = GL_state.Blend(GL_FALSE);
  GLboolean cull = GL_state.CullFace(GL_FALSE);
+  const char *name;
+  float value ;
 
  GL_state.Texture.SetShaderMode(GL_TRUE);
 
@@ -424,10 +426,10 @@
 
  for (size_t idx = 0; idx < Post_effects.size(); idx++) {
  if ( GL_post_shader[Post_active_shader_index].flags2 & (1<<idx) ) {
- const char *name = Post_effects[idx].uniform_name.c_str();
- float value = Post_effects[idx].intensity;
+ name = Post_effects[idx].uniform_name.c_str();
+ value = Post_effects[idx].intensity;
 
- vglUniform1fARB( opengl_shader_get_uniform(name), value);
+ vglUniform1fARB( (name == NULL) ? (-1) : opengl_shader_get_uniform(name), value);
  }
  }
 
Index: code/graphics/gropenglshader.cpp
===================================================================
--- code/graphics/gropenglshader.cpp (revision 10338)
+++ code/graphics/gropenglshader.cpp (working copy)
@@ -704,20 +704,22 @@
  */
 GLint opengl_shader_get_uniform(const char *uniform_text)
 {
- if ( (Current_shader == NULL) || (uniform_text == NULL) ) {
+ if ((Current_shader == NULL)) {
  Int3();
  return -1;
  }
 
  SCP_vector<opengl_shader_uniform_t>::iterator uniform;
- SCP_vector<opengl_shader_uniform_t>::iterator uniforms_end = Current_shader->uniforms.end();
-
- for (uniform = Current_shader->uniforms.begin(); uniform != uniforms_end; ++uniform) {
- if ( !uniform->text_id.compare(uniform_text) ) {
- return uniform->location;
- }
- }
 
+ for (uniform = Current_shader->uniforms.begin(); uniform != Current_shader->uniforms.end(); ++uniform) {
+    if(*(unsigned short *)uniform->text_id.c_str() == *(unsigned short *)uniform_text)
+    {
+      if ( !uniform->text_id.compare(uniform_text) ) {
+        return uniform->location;
+      }
+    }
+  }
+
  return -1;
 }
 
Index: code/math/fvi.cpp
===================================================================
--- code/math/fvi.cpp (revision 10338)
+++ code/math/fvi.cpp (working copy)
@@ -315,75 +315,6 @@
 }
 
 /**
- * Finds intersection of a ray and an axis-aligned bounding box
- *
- * Given a ray with origin at p0, and direction pdir, this function
- * returns non-zero if that ray intersects an axis-aligned bounding box
- * from min to max.   If there was an intersection, then hitpt will contain
- * the point where the ray begins inside the box.
- * Fast ray-box intersection taken from Graphics Gems I, pages 395,736.
- */
-int fvi_ray_boundingbox( vec3d *min, vec3d *max, vec3d * p0, vec3d *pdir, vec3d *hitpt )
-{
- bool inside = true;
- bool middle[3] = { true, true, true };
- int i;
- int which_plane;
- float maxt[3];
- float candidate_plane[3];
-
- for (i = 0; i < 3; i++) {
- if (p0->a1d[i] < min->a1d[i]) {
- candidate_plane[i] = min->a1d[i];
- middle[i] = false;
- inside = false;
- } else if (p0->a1d[i] > max->a1d[i]) {
- candidate_plane[i] = max->a1d[i];
- middle[i] = false;
- inside = false;
- }
- }
-
- // ray origin inside bounding box
- if ( inside ) {
- *hitpt = *p0;
- return 1;
- }
-
- // calculate T distances to candidate plane
- for (i = 0; i < 3; i++) {
- if ( !middle[i] && (pdir->a1d[i] != 0.0f) )
- maxt[i] = (candidate_plane[i] - p0->a1d[i]) / pdir->a1d[i];
- else
- maxt[i] = -1.0f;
- }
-
- // Get largest of the maxt's for final choice of intersection
- which_plane = 0;
- for (i = 1; i < 3; i++) {
- if (maxt[which_plane] < maxt[i])
- which_plane = i;
- }
-
- // check final candidate actually inside box
- if (maxt[which_plane] < 0.0f)
- return 0;
-
- for (i = 0; i < 3; i++) {
- if (which_plane != i) {
- hitpt->a1d[i] = p0->a1d[i] + maxt[which_plane] * pdir->a1d[i];
-
- if ( (hitpt->a1d[i] < min->a1d[i]) || (hitpt->a1d[i] > max->a1d[i]) )
- return 0;
- } else {
- hitpt->a1d[i] = candidate_plane[i];
- }
- }
-
- return 1;
-}
-
-/**
  * Given largest componant of normal, return i & j
  * If largest componant is negative, swap i & j
  */
Index: code/math/fvi.h
===================================================================
--- code/math/fvi.h (revision 10338)
+++ code/math/fvi.h (working copy)
@@ -104,8 +104,77 @@
 // from min to max.   If there was an intersection, then hitpt will contain
 // the point where the ray begins inside the box.
 // Fast ray-box intersection taken from Graphics Gems I, pages 395,736.
-int fvi_ray_boundingbox( vec3d *min, vec3d *max, vec3d * p0, vec3d *pdir, vec3d *hitpt );
+//int fvi_ray_boundingbox( vec3d *min, vec3d *max, vec3d * p0, vec3d *pdir, vec3d *hitpt );
 
+/**
+ * Finds intersection of a ray and an axis-aligned bounding box
+ *
+ * Given a ray with origin at p0, and direction pdir, this function
+ * returns non-zero if that ray intersects an axis-aligned bounding box
+ * from min to max.   If there was an intersection, then hitpt will contain
+ * the point where the ray begins inside the box.
+ * Fast ray-box intersection taken from Graphics Gems I, pages 395,736.
+ */
+inline int fvi_ray_boundingbox( vec3d *min, vec3d *max, vec3d * p0, vec3d *pdir, vec3d *hitpt )
+{
+  bool inside = true;
+  bool middle[3] = { true, true, true };
+  int i;
+  int which_plane;
+  float maxt[3];
+  float candidate_plane[3];
+
+  for (i = 0; i < 3; i++) {
+    if (p0->a1d[i] < min->a1d[i]) {
+      candidate_plane[i] = min->a1d[i];
+      middle[i] = false;
+      inside = false;
+    } else if (p0->a1d[i] > max->a1d[i]) {
+      candidate_plane[i] = max->a1d[i];
+      middle[i] = false;
+      inside = false;
+    }
+  }
+
+  // ray origin inside bounding box
+  if ( inside ) {
+    *hitpt = *p0;
+    return 1;
+  }
+
+  // calculate T distances to candidate plane
+  for (i = 0; i < 3; i++) {
+    if ( !middle[i] && (pdir->a1d[i] != 0.0f) )
+      maxt[i] = (candidate_plane[i] - p0->a1d[i]) / pdir->a1d[i];
+    else
+      maxt[i] = -1.0f;
+  }
+
+  // Get largest of the maxt's for final choice of intersection
+  which_plane = 0;
+  for (i = 1; i < 3; i++) {
+    if (maxt[which_plane] < maxt[i])
+      which_plane = i;
+  }
+
+  // check final candidate actually inside box
+  if (maxt[which_plane] < 0.0f)
+    return 0;
+
+  for (i = 0; i < 3; i++) {
+    if (which_plane != i) {
+      hitpt->a1d[i] = p0->a1d[i] + maxt[which_plane] * pdir->a1d[i];
+
+      if ( (hitpt->a1d[i] < min->a1d[i]) || (hitpt->a1d[i] > max->a1d[i]) )
+        return 0;
+    } else {
+      hitpt->a1d[i] = candidate_plane[i];
+    }
+  }
+
+  return 1;
+}
+
 // sphere polygon collision prototypes
 
 // Given a polygon vertex list and a moving sphere, find the first contact the sphere makes with the edge, if any
Index: code/math/vecmat.cpp
===================================================================
--- code/math/vecmat.cpp (revision 10338)
+++ code/math/vecmat.cpp (working copy)
@@ -318,31 +318,9 @@
 }
 #endif
 
-//returns magnitude of a vector
-float vm_vec_mag(vec3d *v)
-{
- float x,y,z,mag1, mag2;
- x = v->xyz.x*v->xyz.x;
- y = v->xyz.y*v->xyz.y;
- z = v->xyz.z*v->xyz.z;
 
- mag1 = x+y+z;
 
- mag2 = fl_sqrt(mag1);
- return mag2;
-}
 
-//returns squared magnitude of a vector, useful if you want to compare distances
-float vm_vec_mag_squared(vec3d *v)
-{
- float x,y,z,mag1;
- x = v->xyz.x*v->xyz.x;
- y = v->xyz.y*v->xyz.y;
- z = v->xyz.z*v->xyz.z;
- mag1 = x+y+z;
- return mag1;
-}
-
 float vm_vec_dist_squared(vec3d *v0, vec3d *v1)
 {
  float dx, dy, dz;
Index: code/math/vecmat.h
===================================================================
--- code/math/vecmat.h (revision 10338)
+++ code/math/vecmat.h (working copy)
@@ -15,7 +15,7 @@
 #include <float.h>
 #include "globalincs/pstypes.h"
 
-//#define _INLINE_VECMAT
+#define _INLINE_VECMAT
 
 #define vm_is_vec_nan(v) (_isnan((v)->xyz.x) || _isnan((v)->xyz.y) || _isnan((v)->xyz.z))
 
@@ -225,10 +225,33 @@
 void vm_vec_projection_onto_plane (vec3d *projection, vec3d *src, vec3d *normal);
 
 //returns magnitude of a vector
-float vm_vec_mag(vec3d *v);
+//float vm_vec_mag(vec3d *v);
+//returns magnitude of a vector
+inline float vm_vec_mag(vec3d *v)
+{
+  float x,y,z,mag1, mag2;
+  x = v->xyz.x*v->xyz.x;
+  y = v->xyz.y*v->xyz.y;
+  z = v->xyz.z*v->xyz.z;
 
+  mag1 = x+y+z;
+
+  mag2 = fl_sqrt(mag1);
+  return mag2;
+}
+
 // returns the square of the magnitude of a vector (useful if comparing distances)
-float vm_vec_mag_squared(vec3d* v);
+//float vm_vec_mag_squared(vec3d* v);
+//returns squared magnitude of a vector, useful if you want to compare distances
+inline float vm_vec_mag_squared(vec3d *v)
+{
+  float x,y,z,mag1;
+  x = v->xyz.x*v->xyz.x;
+  y = v->xyz.y*v->xyz.y;
+  z = v->xyz.z*v->xyz.z;
+  mag1 = x+y+z;
+  return mag1;
+}
 
 // returns the square of the distance between two points (fast and exact)
 float vm_vec_dist_squared(vec3d *v0, vec3d *v1);
Index: code/model/modelcollide.cpp
===================================================================
--- code/model/modelcollide.cpp (revision 10338)
+++ code/model/modelcollide.cpp (working copy)
@@ -76,7 +76,7 @@
 // Returns non-zero if vector from p0 to pdir
 // intersects the bounding box.
 // hitpos could be NULL, so don't fill it if it is.
-int mc_ray_boundingbox( vec3d *min, vec3d *max, vec3d * p0, vec3d *pdir, vec3d *hitpos )
+inline int mc_ray_boundingbox( vec3d *min, vec3d *max, vec3d * p0, vec3d *pdir, vec3d *hitpos )
 {
 
  vec3d tmp_hitpos;
@@ -481,7 +481,7 @@
 
 int model_collide_sub( void *model_ptr );
 
-void model_collide_sortnorm(ubyte * p)
+inline void model_collide_sortnorm(ubyte * p)
 {
  int frontlist = w(p+36);
  int backlist = w(p+40);
@@ -509,7 +509,7 @@
 
 //calls the object interpreter to render an object.  The object renderer
 //is really a seperate pipeline. returns true if drew
-int model_collide_sub(void *model_ptr )
+inline int model_collide_sub(void *model_ptr )
 {
  ubyte *p = (ubyte *)model_ptr;
  int chunk_type, chunk_size;
@@ -602,7 +602,7 @@
  }
 }
 
-void model_collide_bsp(bsp_collision_tree *tree, int node_index)
+inline void model_collide_bsp(bsp_collision_tree *tree, int node_index)
 {
  if ( tree->node_list == NULL || tree->n_verts <= 0) {
  return;
@@ -949,7 +949,7 @@
  return false;
 }
 
-bool mc_check_sldc(int offset)
+inline bool mc_check_sldc(int offset)
 {
  if (offset > Mc_pm->sldc_size-5) //no way is this big enough
  return false;
@@ -999,7 +999,7 @@
 }
 
 // checks a vector collision against a ships shield (if it has shield points defined).
-void mc_check_shield()
+inline void mc_check_shield()
 {
  int i;
 
@@ -1031,7 +1031,7 @@
 
 // This function recursively checks a submodel and its children
 // for a collision with a vector.
-void mc_check_subobj( int mn )
+inline void mc_check_subobj( int mn )
 {
  vec3d tempv;
  vec3d hitpt; // used in bounding box check
Index: code/object/objcollide.cpp
===================================================================
--- code/object/objcollide.cpp (revision 10338)
+++ code/object/objcollide.cpp (working copy)
@@ -1287,53 +1287,6 @@
  overlapped = true;
 }
 
-float obj_get_collider_endpoint(int obj_num, int axis, bool min)
-{
- if ( Objects[obj_num].type == OBJ_BEAM ) {
- beam *b = &Beams[Objects[obj_num].instance];
-
- // use the last start and last shot as endpoints
- float min_end, max_end;
- if ( b->last_start.a1d[axis] > b->last_shot.a1d[axis] ) {
- min_end = b->last_shot.a1d[axis];
- max_end = b->last_start.a1d[axis];
- } else {
- min_end = b->last_start.a1d[axis];
- max_end = b->last_shot.a1d[axis];
- }
-
- if ( min ) {
- return min_end;
- } else {
- return max_end;
- }
- } else if ( Objects[obj_num].type == OBJ_WEAPON ) {
- float min_end, max_end;
-
- if ( Objects[obj_num].pos.a1d[axis] > Objects[obj_num].last_pos.a1d[axis] ) {
- min_end = Objects[obj_num].last_pos.a1d[axis];
- max_end = Objects[obj_num].pos.a1d[axis];
- } else {
- min_end = Objects[obj_num].pos.a1d[axis];
- max_end = Objects[obj_num].last_pos.a1d[axis];
- }
-
- if ( min ) {
- return min_end - Objects[obj_num].radius;
- } else {
- return max_end + Objects[obj_num].radius;
- }
- } else {
- vec3d *pos = &Objects[obj_num].pos;
-
- if ( min ) {
- return pos->a1d[axis] - Objects[obj_num].radius;
- } else {
- return pos->a1d[axis] + Objects[obj_num].radius;
- }
- }
-}
-
 void obj_quicksort_colliders(SCP_vector<int> *list, int left, int right, int axis)
 {
  Assert( axis >= 0 );
Index: code/object/objcollide.h
===================================================================
--- code/object/objcollide.h (revision 10338)
+++ code/object/objcollide.h (working copy)
@@ -13,6 +13,7 @@
 #define _COLLIDESTUFF_H
 
 #include "globalincs/pstypes.h"
+#include "weapon/beam.h"
 
 class object;
 struct CFILE;
@@ -155,4 +156,54 @@
 int reject_due_collision_groups(object *A, object *B);
 
 void init_collision_info_struct(collision_info_struct *cis);
+
+void obj_collide_pair(object *A, object *B);
+
+inline float obj_get_collider_endpoint(int obj_num, int axis, bool min)
+{
+  if ( Objects[obj_num].type == OBJ_BEAM ) {
+    beam *b = &Beams[Objects[obj_num].instance];
+
+    // use the last start and last shot as endpoints
+    float min_end, max_end;
+    if ( b->last_start.a1d[axis] > b->last_shot.a1d[axis] ) {
+      min_end = b->last_shot.a1d[axis];
+      max_end = b->last_start.a1d[axis];
+    } else {
+      min_end = b->last_start.a1d[axis];
+      max_end = b->last_shot.a1d[axis];
+    }
+
+    if ( min ) {
+      return min_end;
+    } else {
+      return max_end;
+    }
+  } else if ( Objects[obj_num].type == OBJ_WEAPON ) {
+    float min_end, max_end;
+
+    if ( Objects[obj_num].pos.a1d[axis] > Objects[obj_num].last_pos.a1d[axis] ) {
+      min_end = Objects[obj_num].last_pos.a1d[axis];
+      max_end = Objects[obj_num].pos.a1d[axis];
+    } else {
+      min_end = Objects[obj_num].pos.a1d[axis];
+      max_end = Objects[obj_num].last_pos.a1d[axis];
+    }
+
+    if ( min ) {
+      return min_end - Objects[obj_num].radius;
+    } else {
+      return max_end + Objects[obj_num].radius;
+    }
+  } else {
+    vec3d *pos = &Objects[obj_num].pos;
+
+    if ( min ) {
+      return pos->a1d[axis] - Objects[obj_num].radius;
+    } else {
+      return pos->a1d[axis] + Objects[obj_num].radius;
+    }
+  }
+}
+
 #endif
Index: code/object/object.cpp
===================================================================
--- code/object/object.cpp (revision 10338)
+++ code/object/object.cpp (working copy)
@@ -1645,33 +1645,6 @@
  }
 }
 
-/**
- * Do client-side post-interpolation object movement
- */
-void obj_client_post_interpolate()
-{
- object *objp;
-
- // After all objects have been moved, move all docked objects.
- objp = GET_FIRST(&obj_used_list);
- while( objp !=END_OF_LIST(&obj_used_list) ) {
- if ( objp != Player_obj ) {
- dock_move_docked_objects(objp);
- }
- objp = GET_NEXT(objp);
- }
-
- // check collisions
- if ( Cmdline_old_collision_sys ) {
- obj_check_all_collisions();
- } else {
- obj_sort_and_collide();
- }
-
- // do post-collision stuff for beam weapons
- beam_move_all_post();
-}
-
 void obj_observer_move(float frame_time)
 {
  object *objp;
Index: code/object/object.h
===================================================================
--- code/object/object.h (revision 10338)
+++ code/object/object.h (working copy)
@@ -336,4 +336,8 @@
 int obj_get_by_signature(int sig);
 int object_get_model(object *objp);
 
+void obj_move_all_pre(object *objp, float frametime);
+
+void obj_check_object( object *obj );
+
 #endif
Index: code/osapi/osapi_unix.cpp
===================================================================
--- code/osapi/osapi_unix.cpp (revision 10338)
+++ code/osapi/osapi_unix.cpp (working copy)
@@ -14,6 +14,9 @@
 #include <stdio.h>
 #include <fcntl.h>
 #include <stdarg.h>
+#ifdef __linux__
+#include <execinfo.h>
+#endif
 
 #include "globalincs/pstypes.h"
 #include "io/key.h"
@@ -237,8 +240,30 @@
 
 void debug_int3(char *file, int line)
 {
- mprintf(("Int3(): From %s at line %d\n", file, line));
+#ifndef NDEBUG
+#ifdef __linux__
+#define SIZE 1024
+  char **symbols;
+  int i, numstrings;
+  void *buffer[SIZE];
+#endif
+#endif
+  mprintf(("Int3(): From %s at line %d\n", file, line));
 
+#ifndef NDEBUG
+#ifdef __linux__
+ numstrings = backtrace(buffer, SIZE);
+ symbols = backtrace_symbols(buffer, numstrings);
+ if(symbols != NULL)
+ {
+   for(i = 0; i < numstrings; i++)
+   {
+     mprintf(("%s\n", symbols[i]));
+   }
+ }
+ free(symbols);
+#endif
+#endif
  // we have to call os_deinit() before abort() so we make sure that SDL gets
  // closed out and we don't lose video/input control
  os_deinit();
Index: configure.ac
===================================================================
--- configure.ac (revision 10338)
+++ configure.ac (working copy)
@@ -219,7 +219,7 @@
 if test "$fs2_debug" = "yes" ; then
  AC_DEFINE([_DEBUG])
  D_CFLAGS="$D_CFLAGS -O0 -g -Wall -Wextra -Wno-unused-parameter -Wno-write-strings -Wshadow -funroll-loops"
- D_LDFLAGS="$D_LDFLAGS -g"
+ D_LDFLAGS="$D_LDFLAGS -g -rdynamic"
 
  if test "$fs2_fred" = "yes" ; then
  AC_SUBST(FS2_BINARY, ["wxFRED2_${PACKAGE_VERSION}_DEBUG"])

[attachment deleted by an evil time traveler]

 

Offline m!m

  • 211
Re: Performance Improvements (Updated 25/01/14)
I did some performance testing with the WiH opening cutscene: