ソースを参照

add support for libsquish

David Rose 17 年 前
コミット
b37a30fe63

+ 7 - 0
dtool/Config.pp

@@ -470,6 +470,13 @@
 // drfftw instead of rfftw.
 #defer HAVE_DRFFTW_H $[libtest $[FFTW_LPATH],drfftw]
 
+// Is libsquish installed, and where?
+#define SQUISH_IPATH /usr/local/include
+#define SQUISH_LPATH /usr/local/lib
+#define SQUISH_LIBS squish
+#defer HAVE_SQUISH $[libtest $[SQUISH_LPATH],$[SQUISH_LIBS]]
+
+
 // Is Berkeley DB installed, and where?  Presently, this is only used
 // for some applications (egg-optchar in particular) in Pandatool, and
 // it is completely optional there.  If available, egg-optchar takes

+ 8 - 0
dtool/LocalSetup.pp

@@ -41,6 +41,11 @@
 #else
 #print - Did not find fftw
 #endif
+#if $[HAVE_SQUISH]
+#print + squish
+#else
+#print - Did not find squish
+#endif
 #if $[HAVE_CG]
 #print + Nvidia Cg High Level Shading Language
 #else
@@ -232,6 +237,9 @@ $[cdefine HAVE_TAR]
 /* Define if we have libfftw installed.  */
 $[cdefine HAVE_FFTW]
 
+/* Define if we have libsquish installed.  */
+$[cdefine HAVE_SQUISH]
+
 /* Define if we have Berkeley DB installed.  */
 $[cdefine HAVE_BDB]
 

+ 5 - 0
dtool/Package.pp

@@ -145,6 +145,11 @@
 #set FFTW_LIBS $[FFTW_LIBS]
 #set HAVE_FFTW $[HAVE_FFTW]
 
+#set SQUISH_IPATH $[unixfilename $[SQUISH_IPATH]]
+#set SQUISH_LPATH $[unixfilename $[SQUISH_LPATH]]
+#set SQUISH_LIBS $[SQUISH_LIBS]
+#set HAVE_SQUISH $[HAVE_SQUISH]
+
 #set BDB_IPATH $[unixfilename $[BDB_IPATH]]
 #set BDB_LPATH $[unixfilename $[BDB_LPATH]]
 #set BDB_LIBS $[BDB_LIBS]

+ 7 - 0
dtool/pptempl/Global.pp

@@ -241,6 +241,13 @@
   #define fftw_libs $[FFTW_LIBS]
 #endif
 
+#if $[HAVE_SQUISH]
+  #define squish_ipath $[wildcard $[SQUISH_IPATH]]
+  #define squish_lpath $[wildcard $[SQUISH_LPATH]]
+  #define squish_cflags $[SQUISH_CFLAGS]
+  #define squish_libs $[SQUISH_LIBS]
+#endif
+
 #if $[HAVE_BDB]
   #define bdb_ipath $[wildcard $[BDB_IPATH]]
   #define bdb_lpath $[wildcard $[BDB_LPATH]]

+ 5 - 3
panda/src/display/graphicsStateGuardian.I

@@ -419,13 +419,15 @@ get_supports_compressed_texture() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::get_supports_compressed_texture_format
-//       Access: Published
+//       Access: Published, Virtual
 //  Description: Returns true if this GSG can accept textures
 //               pre-compressed in the indicated format.
+//               compression_mode may be any of the
+//               Texture::CompressionMode enums.
 ////////////////////////////////////////////////////////////////////
 INLINE bool GraphicsStateGuardian::
-get_supports_compressed_texture_format(Texture::CompressionMode compression) const {
-  return _compressed_texture_formats.get_bit(compression);
+get_supports_compressed_texture_format(int compression_mode) const {
+  return _compressed_texture_formats.get_bit(compression_mode);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/display/graphicsStateGuardian.h

@@ -122,7 +122,7 @@ PUBLISHED:
   INLINE bool get_supports_tex_non_pow2() const;
 
   INLINE bool get_supports_compressed_texture() const;
-  INLINE bool get_supports_compressed_texture_format(Texture::CompressionMode compression) const;
+  virtual INLINE bool get_supports_compressed_texture_format(int compression_mode) const;
 
   INLINE int get_max_lights() const;
   INLINE int get_max_clip_planes() const;

+ 1 - 1
panda/src/gobj/Sources.pp

@@ -1,7 +1,7 @@
 #define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \
                    dtoolutil:c dtoolbase:c dtool:m prc:c
 //#define OSX_SYS_LIBS mx
-#define USE_PACKAGES zlib cg
+#define USE_PACKAGES zlib cg squish
 
 #begin lib_target
   #define TARGET gobj

+ 11 - 0
panda/src/gobj/config_gobj.cxx

@@ -106,6 +106,17 @@ ConfigVariableBool compressed_textures
 	  "changes the meaning of set_compression(Texture::CM_default) to "
 	  "Texture::CM_on."));
 
+ConfigVariableBool cpu_compress_textures
+("cpu-compress-textures", false,
+ PRC_DESC("Set this true to use the squish library to compress textures on "
+          "the CPU, as they are loaded, rather than to hand them off to "
+          "the graphics driver to compress them.  This will be done "
+          "only if the graphics window is already open and is the default "
+          "graphics context, and it claims to support DXT1/3/5 "
+          "compression.  If any of this is not true, the texture will "
+          "not be automatically compressed via squish, but it may still "
+          "be compressed by the graphics driver."));
+
 ConfigVariableBool vertex_buffers
 ("vertex-buffers", true,
  PRC_DESC("Set this true to allow the use of vertex buffers (or buffer "

+ 1 - 0
panda/src/gobj/config_gobj.h

@@ -54,6 +54,7 @@ extern EXPCL_PANDA_GOBJ ConfigVariableList exclude_texture_scale;
 
 extern EXPCL_PANDA_GOBJ ConfigVariableBool keep_texture_ram;
 extern EXPCL_PANDA_GOBJ ConfigVariableBool compressed_textures;
+extern EXPCL_PANDA_GOBJ ConfigVariableBool cpu_compress_textures;
 extern EXPCL_PANDA_GOBJ ConfigVariableBool vertex_buffers;
 extern EXPCL_PANDA_GOBJ ConfigVariableBool vertex_arrays;
 extern EXPCL_PANDA_GOBJ ConfigVariableBool display_lists;

+ 60 - 2
panda/src/gobj/texture.I

@@ -253,7 +253,11 @@ load(const PNMImage &pnmimage, const LoaderOptions &options) {
   do_clear();
   ++_properties_modified;
   ++_image_modified;
-  return do_load_one(pnmimage, get_name(), 0, 0, options);
+  if (do_load_one(pnmimage, get_name(), 0, 0, options)) {
+    consider_auto_compress_ram_image();
+    return true;
+  }
+  return false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -267,7 +271,11 @@ load(const PNMImage &pnmimage, int z, int n, const LoaderOptions &options) {
   MutexHolder holder(_lock);
   ++_properties_modified;
   ++_image_modified;
-  return do_load_one(pnmimage, get_name(), z, n, options);
+  if (do_load_one(pnmimage, get_name(), z, n, options)) {
+    consider_auto_compress_ram_image();
+    return true;
+  }
+  return false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1205,6 +1213,56 @@ set_keep_ram_image(bool keep_ram_image) {
   _keep_ram_image = keep_ram_image;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::compress_ram_image
+//       Access: Published
+//  Description: Attempts to compress the texture's RAM image
+//               internally, to a format supported by the indicated
+//               GSG.  In order for this to work, the squish library
+//               must have been compiled into Panda.
+//
+//               If compression is CM_on, then an appropriate
+//               compression method that is supported by the indicated
+//               GSG is automatically chosen.  If the GSG pointer is
+//               NULL, any of the standard DXT1/3/5 compression
+//               methods will be used, regardless of whether it is
+//               supported.
+//
+//               If compression is any specific compression method,
+//               that method is used regardless of whether the GSG
+//               supports it.
+//
+//               quality_level determines the speed/quality tradeoff
+//               of the compression.  If it is QL_default, the
+//               texture's own quality_level parameter is used.
+//
+//               Returns true if successful, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool Texture::
+compress_ram_image(Texture::CompressionMode compression,
+                   Texture::QualityLevel quality_level,
+                   GraphicsStateGuardianBase *gsg) {
+  MutexHolder holder(_lock);
+  return do_compress_ram_image(compression, quality_level, gsg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::uncompress_ram_image
+//       Access: Published
+//  Description: Attempts to uncompress the texture's RAM image
+//               internally.  In order for this to work, the squish
+//               library must have been compiled into Panda, and the
+//               ram image must be compressed in a format supported by
+//               squish.
+//
+//               Returns true if successful, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool Texture::
+uncompress_ram_image() {
+  MutexHolder holder(_lock);
+  return do_uncompress_ram_image();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::get_num_ram_mipmap_images
 //       Access: Published

+ 386 - 2
panda/src/gobj/texture.cxx

@@ -39,13 +39,17 @@
 #include "streamReader.h"
 #include "texturePeeker.h"
 
+#ifdef HAVE_SQUISH
+#include <squish.h>
+#endif  // HAVE_SQUISH
+
 #include <stddef.h>
 
 ConfigVariableEnum<Texture::QualityLevel> texture_quality_level
 ("texture-quality-level", Texture::QL_normal,
  PRC_DESC("This specifies a global quality level for all textures.  You "
           "may specify either fastest, normal, or best.  This actually "
-          "affects the meaning of Texture::set_quality_level(TQL_default), "
+          "affects the meaning of Texture::set_quality_level(QL_default), "
           "so it may be overridden on a per-texture basis.  This generally "
           "only has an effect when using the tinydisplay software renderer; "
           "it has little or no effect on normal, hardware-accelerated "
@@ -2082,6 +2086,12 @@ do_read(const Filename &fullpath, const Filename &alpha_fullpath,
     // information, don't let the Texture think that it's got the
     // image now.
     do_clear_ram_image();
+  } else {
+    if ((options.get_texture_flags() & LoaderOptions::TF_preload) != 0) {
+      // If we intend to keep the ram image around, consider
+      // compressing it.
+      consider_auto_compress_ram_image();
+    }
   }
 
   return true;
@@ -2472,6 +2482,7 @@ do_read_txo(istream &in, const string &filename) {
     return false;
   }
 
+  Namable::operator = (*other);
   do_assign(*other);
   _loaded_from_image = true;
   _loaded_from_txo = true;
@@ -2798,7 +2809,7 @@ do_write_one(const Filename &fullpath, int z, int n) const {
 bool Texture::
 do_store_one(PNMImage &pnmimage, int z, int n) const {
   // First, reload the ram image if necessary.
-  ((Texture *)this)->do_get_ram_image();
+  ((Texture *)this)->do_get_uncompressed_ram_image();
 
   nassertr(do_has_ram_mipmap_image(n), false);
   nassertr(z >= 0 && z < do_get_expected_mipmap_z_size(n), false);
@@ -3009,6 +3020,17 @@ do_reload_ram_image(bool allow_compression) {
           _ram_image_compression = tex->_ram_image_compression;
           _ram_images = tex->_ram_images;
           _loaded_from_image = true;
+
+          if (allow_compression && consider_auto_compress_ram_image()) {
+            if (cache->get_cache_compressed_textures()) {
+              // We've re-compressed the image after loading it from the
+              // cache.  To keep the cache current, rewrite it to the
+              // cache now, in its newly compressed form.
+              record->set_data(this, false);
+              cache->store(record);
+            }
+          }
+
           return;
         }
       }
@@ -3122,6 +3144,180 @@ do_make_ram_mipmap_image(int n) {
   return _ram_images[n]._image;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::consider_auto_compress_ram_image
+//       Access: Protected
+//  Description: Should be called after a texture has been loaded into
+//               RAM, this considers compressing the RAM image, if
+//               cpu-compress-textures has been set and the default
+//               GSG has been set and supports it.
+
+//               Returns true if the image was modified by this
+//               operation, false if it wasn't.
+////////////////////////////////////////////////////////////////////
+bool Texture::
+consider_auto_compress_ram_image() {
+  if (cpu_compress_textures) {
+    CompressionMode compression = _compression;
+    if (compression == CM_default) {
+      if (!compressed_textures) {
+        return false;
+      }
+      compression = CM_on;
+    }
+    if (compression != CM_off && _ram_image_compression == CM_off) {
+      GraphicsStateGuardianBase *gsg = GraphicsStateGuardianBase::get_default_gsg();
+      if (gsg != (GraphicsStateGuardianBase *)NULL) {
+        if (do_compress_ram_image(compression, QL_default, gsg)) {
+          gobj_cat.info()
+            << "Compressed " << get_name() << " with " 
+            << _ram_image_compression << "\n";
+          return true;
+        }
+      }
+    }
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::do_compress_ram_image
+//       Access: Protected
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool Texture::
+do_compress_ram_image(Texture::CompressionMode compression,
+                      Texture::QualityLevel quality_level,
+                      GraphicsStateGuardianBase *gsg) {
+  nassertr(compression != CM_off, false);
+
+  if (compression == CM_on) {
+    // Select an appropriate compression mode automatically.
+    switch (_format) {
+    case Texture::F_rgbm:
+    case Texture::F_rgb:
+    case Texture::F_rgb5:
+    case Texture::F_rgba5:
+    case Texture::F_rgb8:
+    case Texture::F_rgb12:
+    case Texture::F_rgb332:
+      if (gsg == NULL || gsg->get_supports_compressed_texture_format(CM_dxt1)) {
+        compression = CM_dxt1;
+      } else if (gsg == NULL || gsg->get_supports_compressed_texture_format(CM_dxt3)) {
+        compression = CM_dxt3;
+      } else if (gsg == NULL || gsg->get_supports_compressed_texture_format(CM_dxt5)) {
+        compression = CM_dxt5;
+      }
+      break;
+      
+    case Texture::F_rgba4:
+      if (gsg == NULL || gsg->get_supports_compressed_texture_format(CM_dxt3)) {
+        compression = CM_dxt3;
+      } else if (gsg == NULL || gsg->get_supports_compressed_texture_format(CM_dxt5)) {
+        compression = CM_dxt5;
+      }
+      break;
+      
+    case Texture::F_rgba:
+    case Texture::F_rgba8:
+    case Texture::F_rgba12:
+      if (gsg == NULL || gsg->get_supports_compressed_texture_format(CM_dxt5)) {
+        compression = CM_dxt5;
+      }
+      break;
+    }
+  }
+
+  // Choose an appropriate quality level.
+  if (quality_level == Texture::QL_default) {
+    quality_level = _quality_level;
+  }
+  if (quality_level == Texture::QL_default) {
+    quality_level = texture_quality_level;
+  }
+
+#ifdef HAVE_SQUISH
+  if (_texture_type != TT_3d_texture && _component_type == T_unsigned_byte) {
+    int squish_flags = 0;
+    switch (compression) {
+    case CM_dxt1:
+      squish_flags |= squish::kDxt1;
+      break;
+
+    case CM_dxt3:
+      squish_flags |= squish::kDxt3;
+      break;
+
+    case CM_dxt5:
+      squish_flags |= squish::kDxt5;
+      break;
+    }
+    
+    if (squish_flags != 0) {
+      // This compression mode is supported by squish; use it.
+      switch (quality_level) {
+      case QL_fastest:
+        squish_flags |= squish::kColourRangeFit;
+        break;
+        
+      case QL_normal:
+        // ColourClusterFit is just too slow for everyday use.
+        squish_flags |= squish::kColourRangeFit;
+        // squish_flags |= squish::kColourClusterFit;
+        break;
+        
+      case QL_best:
+        squish_flags |= squish::kColourIterativeClusterFit;
+        break;
+      }
+      
+      if (do_squish(compression, squish_flags)) {
+        return true;
+      }
+    }
+  }
+#endif  // HAVE_SQUISH
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::do_uncompress_ram_image
+//       Access: Protected
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool Texture::
+do_uncompress_ram_image() {
+
+#ifdef HAVE_SQUISH
+  if (_texture_type != TT_3d_texture && _component_type == T_unsigned_byte) {
+    int squish_flags = 0;
+    switch (_ram_image_compression) {
+    case CM_dxt1:
+      squish_flags |= squish::kDxt1;
+      break;
+
+    case CM_dxt3:
+      squish_flags |= squish::kDxt3;
+      break;
+
+    case CM_dxt5:
+      squish_flags |= squish::kDxt5;
+      break;
+    }
+    
+    if (squish_flags != 0) {
+      // This compression mode is supported by squish; use it.
+      if (do_unsquish(squish_flags)) {
+        return true;
+      }
+    }
+  }
+#endif  // HAVE_SQUISH
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::do_reconsider_z_size
 //       Access: Protected
@@ -3660,6 +3856,17 @@ do_get_ram_image() {
 ////////////////////////////////////////////////////////////////////
 CPTA_uchar Texture::
 do_get_uncompressed_ram_image() {
+  if (!_ram_images.empty() && _ram_image_compression != CM_off) {
+    // We have an image in-ram, but it's compressed.  Try to
+    // uncompress it first.
+    if (do_uncompress_ram_image()) {
+      gobj_cat.info()
+        << "Uncompressed " << get_name() << "\n";
+      return _ram_images[0]._image;
+    }
+  }
+
+  // Couldn't uncompress the existing image.  Try to reload it.
   if (_loaded_from_image && (!do_has_ram_image() || _ram_image_compression != CM_off) && !_fullpath.empty()) {
     do_unlock_and_reload_ram_image(false);
   }
@@ -5094,6 +5301,183 @@ filter_3d_unsigned_short(unsigned char *&p, const unsigned char *&q,
   q += 2;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::do_squish
+//       Access: Private
+//  Description: Invokes the squish library to compress the RAM
+//               image(s).
+////////////////////////////////////////////////////////////////////
+bool Texture::
+do_squish(Texture::CompressionMode compression, int squish_flags) {
+#ifdef HAVE_SQUISH
+  if (_ram_images.empty()) {
+    return false;
+  }
+  RamImages compressed_ram_images;
+  compressed_ram_images.reserve(_ram_images.size());
+  for (size_t n = 0; n < _ram_images.size(); ++n) {
+    RamImage compressed_image;
+    int x_size = do_get_expected_mipmap_x_size(n);
+    int y_size = do_get_expected_mipmap_y_size(n);
+    int z_size = do_get_expected_mipmap_z_size(n);
+    int page_size = squish::GetStorageRequirements(x_size, y_size, squish_flags);
+    int cell_size = squish::GetStorageRequirements(4, 4, squish_flags);
+
+    compressed_image._page_size = page_size;
+    compressed_image._image = PTA_uchar::empty_array(page_size * z_size);
+    for (int z = 0; z < z_size; ++z) {
+      unsigned char *dest_page = compressed_image._image.p() + z * page_size;
+      unsigned const char *source_page = _ram_images[n]._image.p() + z * _ram_images[n]._page_size;
+      unsigned const char *source_page_end = source_page + _ram_images[n]._page_size;
+      // Convert one 4 x 4 cell at a time.
+      unsigned char *d = dest_page;
+      for (int y = 0; y < y_size; y += 4) {
+        for (int x = 0; x < x_size; x += 4) {
+          unsigned char tb[16 * 4];
+          int mask = 0;
+          unsigned char *t = tb;
+          for (int i = 0; i < 16; ++i) {
+            int xi = x + i % 4;
+            int yi = y + i / 4;
+            unsigned const char *s = source_page + (yi * x_size + xi) * _num_components;
+            if (s < source_page_end) {
+              switch (_num_components) {
+              case 1:
+                t[0] = s[0];   // r
+                t[1] = s[0];   // g
+                t[2] = s[0];   // b
+                t[3] = 255;    // a
+                break;
+
+              case 2:
+                t[0] = s[0];   // r
+                t[1] = s[0];   // g
+                t[2] = s[0];   // b
+                t[3] = s[1];   // a
+                break;
+
+              case 3:
+                t[0] = s[2];   // r
+                t[1] = s[1];   // g
+                t[2] = s[0];   // b
+                t[3] = 255;    // a
+                break;
+
+              case 4:
+                t[0] = s[2];   // r
+                t[1] = s[1];   // g
+                t[2] = s[0];   // b
+                t[3] = s[3];   // a
+                break;
+              }
+              mask |= (1 << i);
+            }
+            t += 4;
+          }
+          squish::CompressMasked(tb, mask, d, squish_flags);
+          d += cell_size;
+          Thread::consider_yield();
+        }
+      }
+    }
+    compressed_ram_images.push_back(compressed_image);
+  }
+  _ram_images.swap(compressed_ram_images);
+  _ram_image_compression = compression;
+  ++_image_modified;
+  return true;
+
+#else  // HAVE_SQUISH
+  return false;
+
+#endif  // HAVE_SQUISH
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::do_unsquish
+//       Access: Private
+//  Description: Invokes the squish library to uncompress the RAM
+//               image(s).
+////////////////////////////////////////////////////////////////////
+bool Texture::
+do_unsquish(int squish_flags) {
+#ifdef HAVE_SQUISH
+  if (_ram_images.empty()) {
+    return false;
+  }
+  RamImages uncompressed_ram_images;
+  uncompressed_ram_images.reserve(_ram_images.size());
+  for (size_t n = 0; n < _ram_images.size(); ++n) {
+    RamImage uncompressed_image;
+    int x_size = do_get_expected_mipmap_x_size(n);
+    int y_size = do_get_expected_mipmap_y_size(n);
+    int z_size = do_get_expected_mipmap_z_size(n);
+    int page_size = squish::GetStorageRequirements(x_size, y_size, squish_flags);
+    int cell_size = squish::GetStorageRequirements(4, 4, squish_flags);
+
+    uncompressed_image._page_size = do_get_expected_ram_mipmap_page_size(n);
+    uncompressed_image._image = PTA_uchar::empty_array(uncompressed_image._page_size * z_size);
+    for (int z = 0; z < z_size; ++z) {
+      unsigned char *dest_page = uncompressed_image._image.p() + z * uncompressed_image._page_size;
+      unsigned char *dest_page_end = dest_page + uncompressed_image._page_size;
+      unsigned const char *source_page = _ram_images[n]._image.p() + z * page_size;
+      // Unconvert one 4 x 4 cell at a time.
+      unsigned const char *s = source_page;
+      for (int y = 0; y < y_size; y += 4) {
+        for (int x = 0; x < x_size; x += 4) {
+          unsigned char tb[16 * 4];
+          squish::Decompress(tb, s, squish_flags);
+          s += cell_size;
+
+          unsigned char *t = tb;
+          for (int i = 0; i < 16; ++i) {
+            int xi = x + i % 4;
+            int yi = y + i / 4;
+            unsigned char *d = dest_page + (yi * x_size + xi) * _num_components;
+            if (d < dest_page_end) {
+              switch (_num_components) {
+              case 1:
+                d[0] = t[1];   // g
+                break;
+
+              case 2:
+                d[0] = t[1];   // g
+                d[1] = t[3];   // a
+                break;
+
+              case 3:
+                d[2] = t[0];   // r
+                d[1] = t[1];   // g
+                d[0] = t[2];   // b
+                break;
+
+              case 4:
+                d[2] = t[0];   // r
+                d[1] = t[1];   // g
+                d[0] = t[2];   // b
+                d[3] = t[3];   // a
+                break;
+              }
+            }
+            t += 4;
+          }
+        }
+        Thread::consider_yield();
+      }
+    }
+    uncompressed_ram_images.push_back(uncompressed_image);
+  }
+  _ram_images.swap(uncompressed_ram_images);
+  _ram_image_compression = CM_off;
+  ++_image_modified;
+  return true;
+
+#else  // HAVE_SQUISH
+  return false;
+
+#endif  // HAVE_SQUISH
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::register_with_read_factory
 //       Access: Public, Static

+ 14 - 0
panda/src/gobj/texture.h

@@ -313,6 +313,11 @@ PUBLISHED:
   INLINE void set_keep_ram_image(bool keep_ram_image);
   virtual bool get_keep_ram_image() const;
 
+  INLINE bool compress_ram_image(CompressionMode compression = CM_on,
+                                 QualityLevel quality_level = QL_default,
+                                 GraphicsStateGuardianBase *gsg = NULL);
+  INLINE bool uncompress_ram_image();
+
   INLINE int get_num_ram_mipmap_images() const;
   INLINE bool has_ram_mipmap_image(int n) const;
   int get_num_loadable_ram_mipmap_images() const;
@@ -475,6 +480,12 @@ protected:
   PTA_uchar do_modify_ram_mipmap_image(int n);
   PTA_uchar do_make_ram_mipmap_image(int n);
 
+  bool consider_auto_compress_ram_image();
+  bool do_compress_ram_image(CompressionMode compression,
+                             QualityLevel quality_level,
+                             GraphicsStateGuardianBase *gsg);
+  bool do_uncompress_ram_image();
+
   bool do_reconsider_z_size(int z);
   bool do_reconsider_image_properties(int x_size, int y_size, int num_components,
                                       ComponentType component_type, int z,
@@ -607,6 +618,9 @@ private:
                                        const unsigned char *&q,
                                        size_t pixel_size, size_t row_size,
                                        size_t page_size);
+  
+  bool do_squish(CompressionMode compression, int squish_flags);
+  bool do_unsquish(int squish_flags);
 
 protected:
   // Protects all of the members of this class.

+ 10 - 0
panda/src/gobj/texturePool.cxx

@@ -920,6 +920,16 @@ try_load_cache(PT(Texture) &tex, BamCache *cache, const Filename &filename,
             if (!(options.get_texture_flags() & LoaderOptions::TF_preload)) {
               // But drop the RAM until we need it.
               tex->clear_ram_image();
+
+            } else if (tex->consider_auto_compress_ram_image()) {
+              if (cache->get_cache_compressed_textures()) {
+                // We've re-compressed the image after loading it from the
+                // cache.  To keep the cache current, rewrite it to the
+                // cache now, in its newly compressed form.
+                record->set_data(tex, false);
+                cache->store(record);
+                compressed_cache_record = true;
+              }
             }
             tex->set_keep_ram_image(false);
           }

+ 1 - 0
panda/src/gsgbase/graphicsStateGuardianBase.h

@@ -115,6 +115,7 @@ PUBLISHED:
   virtual int get_max_vertices_per_primitive() const=0;
 
   virtual int get_max_texture_dimension() const=0;
+  virtual bool get_supports_compressed_texture_format(int compression_mode) const=0;
 
   virtual bool get_supports_multisample() const=0;
   virtual int get_supported_geom_rendering() const=0;

+ 3 - 2
panda/src/putil/bamCache.I

@@ -101,8 +101,9 @@ get_cache_textures() const {
 //       Access: Published
 //  Description: Indicates whether compressed texture files will be
 //               stored in the cache, as compressed txo files.  The
-//               compression data is extracted from the GSG after the
-//               texture has been loaded.
+//               compressed data may either be generated in-CPU, via
+//               the squish library, or it may be extracted from the
+//               GSG after the texture has been loaded.
 //
 //               This may be set in conjunction with
 //               set_cache_textures(), or independently of it.  If

+ 7 - 2
panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx

@@ -1597,17 +1597,22 @@ prepare_texture(Texture *tex) {
   default:
     // Anything else is not supported.
     tinydisplay_cat.info()
-      << "not loading texture " << tex->get_name() << ": "
+      << "Not loading texture " << tex->get_name() << ": "
       << tex->get_texture_type() << "\n";
     return NULL;
   }
 
+  // Even though the texture might be compressed now, it might have an
+  // available uncompressed version that we can load.  So don't reject
+  // it out-of-hand just because it's compressed.
+  /*
   if (tex->get_ram_image_compression() != Texture::CM_off) {
     tinydisplay_cat.info()
-      << "not loading texture " << tex->get_name() << ": "
+      << "Not loading texture " << tex->get_name() << ": "
       << tex->get_ram_image_compression() << "\n";
     return NULL;
   }
+  */
 
   TinyTextureContext *gtc = new TinyTextureContext(_prepared_objects, tex);