Browse Source

gobj: Fix TexturePool sharing texture objects when loaded with different properties

Fixes #1047
Closes #1105

Co-authored-by: rdb <[email protected]>
Timothy Paustian 2 years ago
parent
commit
4fa90bd754

+ 188 - 151
panda/src/egg2pg/eggLoader.cxx

@@ -96,6 +96,7 @@
 #include "uvScrollNode.h"
 #include "textureStagePool.h"
 #include "cmath.h"
+#include "loaderOptions.h"
 
 #include <ctype.h>
 #include <algorithm>
@@ -879,6 +880,7 @@ load_texture(TextureDef &def, EggTexture *egg_tex) {
   // Check to see if we should reduce the number of channels in the texture.
   int wanted_channels = 0;
   bool wanted_alpha = false;
+
   switch (egg_tex->get_format()) {
   case EggTexture::F_red:
   case EggTexture::F_green:
@@ -932,6 +934,7 @@ load_texture(TextureDef &def, EggTexture *egg_tex) {
 
   // By convention, the egg loader will preload the simple texture images.
   LoaderOptions options;
+
   if (egg_preload_simple_textures) {
     options.set_texture_flags(options.get_texture_flags() | LoaderOptions::TF_preload_simple);
   }
@@ -963,6 +966,15 @@ load_texture(TextureDef &def, EggTexture *egg_tex) {
     options.set_texture_flags(options.get_texture_flags() | LoaderOptions::TF_allow_compression);
   }
 
+  if (egg_force_srgb_textures) {
+    options.set_texture_flags(options.get_texture_flags() | LoaderOptions::TF_force_srgb);
+  }
+
+  //The following code sets up all the options for the textures
+  //so that they can be differentiated later in texturePool
+  SamplerState sampler;
+  set_up_loader_options(egg_tex, options, sampler);
+
   PT(Texture) tex;
   switch (egg_tex->get_texture_type()) {
   case EggTexture::TT_unspecified:
@@ -976,22 +988,26 @@ load_texture(TextureDef &def, EggTexture *egg_tex) {
                                       egg_tex->get_alpha_fullpath(),
                                       wanted_channels,
                                       egg_tex->get_alpha_file_channel(),
-                                      egg_tex->get_read_mipmaps(), options);
+                                      egg_tex->get_read_mipmaps(),
+                                      options, sampler);
     } else {
       tex = TexturePool::load_texture(egg_tex->get_fullpath(),
                                       wanted_channels,
-                                      egg_tex->get_read_mipmaps(), options);
+                                      egg_tex->get_read_mipmaps(),
+                                      options, sampler);
     }
     break;
 
   case EggTexture::TT_3d_texture:
     tex = TexturePool::load_3d_texture(egg_tex->get_fullpath(),
-                                       egg_tex->get_read_mipmaps(), options);
+                                       egg_tex->get_read_mipmaps(),
+                                       options, sampler);
     break;
 
   case EggTexture::TT_cube_map:
     tex = TexturePool::load_cube_map(egg_tex->get_fullpath(),
-                                     egg_tex->get_read_mipmaps(), options);
+                                     egg_tex->get_read_mipmaps(),
+                                     options, sampler);
     break;
   }
 
@@ -1030,8 +1046,7 @@ load_texture(TextureDef &def, EggTexture *egg_tex) {
       egg_tex->set_anisotropic_degree(aux_egg_tex->get_anisotropic_degree());
     }
   }
-
-  apply_texture_attributes(tex, egg_tex);
+  check_texture_attributes(tex, sampler, egg_tex);
 
   // Make a texture stage for the texture.
   PT(TextureStage) stage = make_texture_stage(egg_tex);
@@ -1042,18 +1057,26 @@ load_texture(TextureDef &def, EggTexture *egg_tex) {
   return true;
 }
 
-
 /**
- *
+ * Populate the loader options for the incoming texture. These are applied to
+ * the texture and also used to search for the texture. Textures are stored in
+ * texturePool in _textures a map object
  */
 void EggLoader::
-apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) {
-  if (egg_tex->get_compression_mode() != EggTexture::CM_default) {
-    tex->set_compression(convert_compression_mode(egg_tex->get_compression_mode()));
-  }
-
-  SamplerState sampler;
+set_up_loader_options(EggTexture *egg_tex, LoaderOptions &options, SamplerState &sampler) {
+  //We store these options and Texture enums, so we need to convert them.
+  options.set_texture_format(convert_format(egg_tex->get_format(), egg_tex->get_env_type()));
+  options.set_texture_compression(convert_compression_mode(egg_tex->get_compression_mode()));
+  options.set_texture_quality(convert_quality_level(egg_tex->get_quality_level()));
+  set_up_sampler(sampler, egg_tex);
+}
 
+/**
+ * Set up the sampler object that will be stored in the texture pool. Samplers
+ * store the wrap mode, the min and mag filters, and anisotropic degree
+ */
+void EggLoader::
+set_up_sampler(SamplerState &sampler, const EggTexture *egg_tex) {
   EggTexture::WrapMode wrap_u = egg_tex->determine_wrap_u();
   EggTexture::WrapMode wrap_v = egg_tex->determine_wrap_v();
   EggTexture::WrapMode wrap_w = egg_tex->determine_wrap_w();
@@ -1185,202 +1208,216 @@ apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) {
   if (egg_tex->has_lod_bias()) {
     sampler.set_lod_bias(egg_tex->get_lod_bias());
   }
+}
 
-  tex->set_default_sampler(sampler);
-
-  bool force_srgb = false;
-  if (egg_force_srgb_textures) {
-    switch (egg_tex->get_env_type()) {
-    case EggTexture::ET_unspecified:
-    case EggTexture::ET_modulate:
-    case EggTexture::ET_decal:
-    case EggTexture::ET_blend:
-    case EggTexture::ET_replace:
-    case EggTexture::ET_add:
-    case EggTexture::ET_blend_color_scale:
-    case EggTexture::ET_modulate_glow:
-    case EggTexture::ET_modulate_gloss:
-      force_srgb = true;
-      if (egg2pg_cat.is_debug()) {
-        egg2pg_cat.debug()
-          << "Enabling sRGB format on texture " << egg_tex->get_name() << "\n";
-      }
-      break;
-
-    default:
-      break;
-    }
-  }
-
-  if (tex->get_num_components() == 1) {
+/**
+ * Now that the texture is fully loaded determine whether there are any
+ * inconsistencies in the texture vs the attributes.
+ */
+void EggLoader::
+check_texture_attributes(Texture *tex, SamplerState sampler, const EggTexture *egg_tex) {
+  switch (tex->get_num_components()) {
+  case 1:
     switch (egg_tex->get_format()) {
+    case EggTexture::F_unspecified:
     case EggTexture::F_red:
-      tex->set_format(Texture::F_red);
-      break;
     case EggTexture::F_green:
-      tex->set_format(Texture::F_green);
-      break;
     case EggTexture::F_blue:
-      tex->set_format(Texture::F_blue);
-      break;
     case EggTexture::F_alpha:
-      tex->set_format(Texture::F_alpha);
-      break;
     case EggTexture::F_luminance:
-      tex->set_format(force_srgb ? Texture::F_sluminance : Texture::F_luminance);
       break;
-
     default:
       egg2pg_cat.warning()
-        << "Ignoring inappropriate format " << egg_tex->get_format()
+        << "Inappropriate format " << egg_tex->get_format()
         << " for 1-component texture " << egg_tex->get_name() << "\n";
-
-    case EggTexture::F_unspecified:
-      if (force_srgb) {
-        tex->set_format(Texture::F_sluminance);
-      }
-      break;
     }
+    break;
 
-  } else if (tex->get_num_components() == 2) {
+  case 2:
     switch (egg_tex->get_format()) {
+    case EggTexture::F_unspecified:
     case EggTexture::F_luminance_alpha:
-      tex->set_format(force_srgb ? Texture::F_sluminance_alpha : Texture::F_luminance_alpha);
-      break;
-
     case EggTexture::F_luminance_alphamask:
-      tex->set_format(force_srgb ? Texture::F_sluminance_alpha : Texture::F_luminance_alphamask);
       break;
-
     default:
-      egg2pg_cat.warning()
-        << "Ignoring inappropriate format " << egg_tex->get_format()
+      egg2pg_cat.error()
+        << "Inappropriate format " << egg_tex->get_format()
         << " for 2-component texture " << egg_tex->get_name() << "\n";
-
-    case EggTexture::F_unspecified:
-      if (force_srgb) {
-        tex->set_format(Texture::F_sluminance_alpha);
-      }
-      break;
     }
+    break;
 
-  } else if (tex->get_num_components() == 3) {
+  case 3:
+    // We'll quietly accept RGBA8 for a 3-component texture, since flt2egg
+    // generates these for 3-component as well as for 4-component textures.
     switch (egg_tex->get_format()) {
+    case EggTexture::F_unspecified:
     case EggTexture::F_rgb:
-      tex->set_format(force_srgb ? Texture::F_srgb : Texture::F_rgb);
-      break;
-    case EggTexture::F_rgb12:
-      if (force_srgb) {
-        tex->set_format(Texture::F_srgb);
-      } else if (tex->get_component_width() >= 2) {
-        // Only do this if the component width supports it.
-        tex->set_format(Texture::F_rgb12);
-      } else {
-        egg2pg_cat.warning()
-          << "Ignoring inappropriate format " << egg_tex->get_format()
-          << " for 8-bit texture " << egg_tex->get_name() << "\n";
-      }
-      break;
     case EggTexture::F_rgb8:
     case EggTexture::F_rgba8:
-      // We'll quietly accept RGBA8 for a 3-component texture, since flt2egg
-      // generates these for 3-component as well as for 4-component textures.
-      tex->set_format(force_srgb ? Texture::F_srgb : Texture::F_rgb8);
-      break;
     case EggTexture::F_rgb5:
-      tex->set_format(force_srgb ? Texture::F_srgb : Texture::F_rgb5);
-      break;
     case EggTexture::F_rgb332:
-      tex->set_format(force_srgb ? Texture::F_srgb : Texture::F_rgb332);
-      break;
     case EggTexture::F_srgb:
     case EggTexture::F_srgb_alpha:
-      tex->set_format(Texture::F_srgb);
       break;
-
-    default:
-      egg2pg_cat.warning()
-        << "Ignoring inappropriate format " << egg_tex->get_format()
-        << " for 3-component texture " << egg_tex->get_name() << "\n";
-
-    case EggTexture::F_unspecified:
-      if (force_srgb) {
-        tex->set_format(Texture::F_srgb);
+    case EggTexture::F_rgb12:
+      if (!egg_force_srgb_textures && tex->get_component_width() < 2) {
+        egg2pg_cat.error()
+          << "Inappropriate format " << egg_tex->get_format()
+          << " for 8-bit texture " << egg_tex->get_name() << "\n";
       }
       break;
+    default:
+      egg2pg_cat.error()
+        << "Inappropriate format " << egg_tex->get_format()
+        << " for 3-component texture " << egg_tex->get_name() << "\n";
     }
+    break;
 
-  } else if (tex->get_num_components() == 4) {
+  case 4:
     switch (egg_tex->get_format()) {
+    case EggTexture::F_unspecified:
     case EggTexture::F_rgba:
-      tex->set_format(force_srgb ? Texture::F_srgb_alpha : Texture::F_rgba);
-      break;
     case EggTexture::F_rgbm:
-      tex->set_format(force_srgb ? Texture::F_srgb_alpha : Texture::F_rgbm);
+    case EggTexture::F_rgba8:
+    case EggTexture::F_rgba4:
+    case EggTexture::F_rgba5:
+    case EggTexture::F_srgb_alpha:
       break;
     case EggTexture::F_rgba12:
-      if (force_srgb) {
-        tex->set_format(Texture::F_srgb_alpha);
-      } else if (tex->get_component_width() >= 2) {
-        // Only do this if the component width supports it.
-        tex->set_format(force_srgb ? Texture::F_srgb_alpha : Texture::F_rgba12);
-      } else {
+      if (!egg_force_srgb_textures || tex->get_component_width() < 2) {
         egg2pg_cat.warning()
-          << "Ignoring inappropriate format " << egg_tex->get_format()
+          << "Inappropriate format " << egg_tex->get_format()
           << " for 8-bit texture " << egg_tex->get_name() << "\n";
       }
       break;
-    case EggTexture::F_rgba8:
-      tex->set_format(force_srgb ? Texture::F_srgb_alpha : Texture::F_rgba8);
-      break;
-    case EggTexture::F_rgba4:
-      tex->set_format(force_srgb ? Texture::F_srgb_alpha : Texture::F_rgba4);
-      break;
-    case EggTexture::F_rgba5:
-      tex->set_format(force_srgb ? Texture::F_srgb_alpha : Texture::F_rgba5);
-      break;
-    case EggTexture::F_srgb_alpha:
-      tex->set_format(Texture::F_srgb_alpha);
-      break;
-
     default:
-      egg2pg_cat.warning()
-        << "Ignoring inappropriate format " << egg_tex->get_format()
+      egg2pg_cat.error()
+        << "Inappropriate format " << egg_tex->get_format()
         << " for 4-component texture " << egg_tex->get_name() << "\n";
+    }
+    break;
 
-    case EggTexture::F_unspecified:
-      if (force_srgb) {
-        tex->set_format(Texture::F_srgb_alpha);
-      }
+  default:
+    break;
+  }
+}
+
+/**
+ * Returns the Texture::Format enum corresponding to the EggTexture::Format.
+ * Returns 0 if the compression mode is unspecified.
+ */
+Texture::Format EggLoader::
+convert_format(EggTexture::Format format, EggTexture::EnvType env) {
+  bool force_srgb = false;
+  if (egg_force_srgb_textures) {
+    switch (env) {
+    case EggTexture::ET_unspecified:
+    case EggTexture::ET_modulate:
+    case EggTexture::ET_decal:
+    case EggTexture::ET_blend:
+    case EggTexture::ET_replace:
+    case EggTexture::ET_add:
+    case EggTexture::ET_blend_color_scale:
+    case EggTexture::ET_modulate_glow:
+    case EggTexture::ET_modulate_gloss:
+      force_srgb = true;
       break;
     }
   }
 
-  if (force_srgb && tex->get_format() != Texture::F_alpha &&
-      !Texture::is_srgb(tex->get_format())) {
-    egg2pg_cat.warning()
-      << "Unable to enable sRGB format on texture " << egg_tex->get_name()
-      << " with specified format " << egg_tex->get_format() << "\n";
+  switch (format) {
+  case EggTexture::F_unspecified:
+    // Gets handled in TexturePool
+    return (Texture::Format)0;
+
+  case EggTexture::F_red:
+    return Texture::F_red;
+
+  case EggTexture::F_green:
+    return Texture::F_green;
+
+  case EggTexture::F_blue:
+    return Texture::F_blue;
+
+  case EggTexture::F_alpha:
+    return Texture::F_alpha;
+
+  case EggTexture::F_luminance:
+    return (force_srgb ? Texture::F_sluminance : Texture::F_luminance);
+
+  case EggTexture::F_rgba:
+    return (force_srgb ? Texture::F_srgb_alpha : Texture::F_rgba);
+
+  case EggTexture::F_rgbm:
+    return (force_srgb ? Texture::F_srgb_alpha : Texture::F_rgbm);
+
+  case EggTexture::F_rgba12:
+    return (force_srgb ? Texture::F_srgb_alpha : Texture::F_rgba12);
+
+  case EggTexture::F_rgba8:
+    return (force_srgb ? Texture::F_srgb_alpha : Texture::F_rgba8);
+
+  case EggTexture::F_rgba4:
+    return (force_srgb ? Texture::F_srgb_alpha : Texture::F_rgba4);
+
+  case EggTexture::F_rgba5:
+    return (force_srgb ? Texture::F_srgb : Texture::F_rgba5);
+
+  case EggTexture::F_rgb:
+    return (force_srgb ? Texture::F_srgb : Texture::F_rgb);
+
+  case EggTexture::F_rgb12:
+    return (force_srgb ? Texture::F_srgb : Texture::F_rgb12);
+
+  case EggTexture::F_rgb8:
+    return (force_srgb ? Texture::F_srgb : Texture::F_rgb8);
+
+  case EggTexture::F_rgb5:
+    return (force_srgb ? Texture::F_srgb : Texture::F_rgb5);
+
+  case EggTexture::F_rgb332:
+    return (force_srgb ? Texture::F_srgb : Texture::F_rgb332);
+
+  case EggTexture::F_luminance_alpha:
+    return (force_srgb ? Texture::F_sluminance_alpha : Texture::F_luminance_alpha);
+
+  case EggTexture::F_luminance_alphamask:
+    return (force_srgb ? Texture::F_sluminance_alpha : Texture::F_luminance_alphamask);
+
+  case EggTexture::F_srgb:
+    return Texture::F_srgb;
+
+  case EggTexture::F_srgb_alpha:
+    return Texture::F_srgb_alpha;
   }
 
-  switch (egg_tex->get_quality_level()) {
+  egg2pg_cat.warning()
+    << "Unexpected format scalar: " << (int)format << "\n";
+  return (Texture::Format)0;
+}
+
+/**
+ * Returns the Texture::QualityLevel enum corresponding to the
+ * EggTexture::QualityLevel.  Returns QL_default if the quality level is
+ * unspecified.
+ */
+Texture::QualityLevel EggLoader::
+convert_quality_level(EggTexture::QualityLevel quality) {
+  switch (quality) {
   case EggTexture::QL_unspecified:
   case EggTexture::QL_default:
-    tex->set_quality_level(Texture::QL_default);
-    break;
+    return Texture::QL_default;
 
   case EggTexture::QL_fastest:
-    tex->set_quality_level(Texture::QL_fastest);
-    break;
-
-  case EggTexture::QL_normal:
-    tex->set_quality_level(Texture::QL_normal);
-    break;
+    return Texture::QL_fastest;
 
   case EggTexture::QL_best:
-    tex->set_quality_level(Texture::QL_best);
-    break;
+    return Texture::QL_best;
+
+  case EggTexture::QL_normal:
+    return Texture::QL_normal;
   }
+  return Texture::QL_default;
 }
 
 /**

+ 5 - 1
panda/src/egg2pg/eggLoader.h

@@ -118,7 +118,11 @@ private:
 
   void load_textures();
   bool load_texture(TextureDef &def, EggTexture *egg_tex);
-  void apply_texture_attributes(Texture *tex, const EggTexture *egg_tex);
+  void set_up_loader_options(EggTexture *egg_tex, LoaderOptions &options, SamplerState &sampler);
+  void set_up_sampler(SamplerState &sampler, const EggTexture *egg_tex);
+  void check_texture_attributes(Texture *tex, SamplerState sampler, const EggTexture *egg_tex);
+  Texture::Format convert_format(EggTexture::Format format, EggTexture::EnvType env);
+  Texture::QualityLevel convert_quality_level(EggTexture::QualityLevel mode);
   Texture::CompressionMode convert_compression_mode(EggTexture::CompressionMode compression_mode) const;
   SamplerState::WrapMode convert_wrap_mode(EggTexture::WrapMode wrap_mode) const;
   PT(TextureStage) make_texture_stage(const EggTexture *egg_tex);

+ 1 - 2
panda/src/gobj/samplerState.h

@@ -20,9 +20,8 @@
 #include "luse.h"
 #include "memoryBase.h"
 #include "numeric_types.h"
-#include "bamReader.h"
 #include "config_gobj.h"
-
+class BamReader;
 class FactoryParams;
 class GraphicsStateGuardianBase;
 class PreparedGraphicsObjects;

+ 43 - 11
panda/src/gobj/texturePool.I

@@ -68,9 +68,9 @@ get_texture(const Filename &filename, const Filename &alpha_filename,
  */
 INLINE Texture *TexturePool::
 load_texture(const Filename &filename, int primary_file_num_channels,
-             bool read_mipmaps, const LoaderOptions &options) {
+             bool read_mipmaps, const LoaderOptions &options, const SamplerState &sampler) {
   return get_global_ptr()->ns_load_texture(filename, primary_file_num_channels,
-                                           read_mipmaps, options);
+                                           read_mipmaps, options, sampler);
 }
 
 /**
@@ -86,11 +86,11 @@ load_texture(const Filename &filename, int primary_file_num_channels,
 INLINE Texture *TexturePool::
 load_texture(const Filename &filename, const Filename &alpha_filename,
              int primary_file_num_channels, int alpha_file_channel,
-             bool read_mipmaps, const LoaderOptions &options) {
+             bool read_mipmaps, const LoaderOptions &options, const SamplerState &sampler) {
   return get_global_ptr()->ns_load_texture(filename, alpha_filename,
                                            primary_file_num_channels,
                                            alpha_file_channel,
-                                           read_mipmaps, options);
+                                           read_mipmaps, options, sampler);
 }
 
 /**
@@ -105,9 +105,9 @@ load_texture(const Filename &filename, const Filename &alpha_filename,
  */
 INLINE Texture *TexturePool::
 load_3d_texture(const Filename &filename_pattern, bool read_mipmaps,
-                const LoaderOptions &options) {
+                const LoaderOptions &options, const SamplerState &sampler) {
   return get_global_ptr()->ns_load_3d_texture(filename_pattern, read_mipmaps,
-                                              options);
+                                              options, sampler);
 }
 
 /**
@@ -122,9 +122,9 @@ load_3d_texture(const Filename &filename_pattern, bool read_mipmaps,
  */
 INLINE Texture *TexturePool::
 load_2d_texture_array(const Filename &filename_pattern, bool read_mipmaps,
-                const LoaderOptions &options) {
+                const LoaderOptions &options, const SamplerState &sampler) {
   return get_global_ptr()->ns_load_2d_texture_array(filename_pattern, read_mipmaps,
-                                                    options);
+                                                    options, sampler);
 }
 
 /**
@@ -139,9 +139,9 @@ load_2d_texture_array(const Filename &filename_pattern, bool read_mipmaps,
  */
 INLINE Texture *TexturePool::
 load_cube_map(const Filename &filename_pattern, bool read_mipmaps,
-              const LoaderOptions &options) {
+              const LoaderOptions &options, const SamplerState &sampler) {
   return get_global_ptr()->ns_load_cube_map(filename_pattern, read_mipmaps,
-                                            options);
+                                            options, sampler);
 }
 
 /**
@@ -333,11 +333,31 @@ unregister_filter(TexturePoolFilter *tex_filter) {
   return get_global_ptr()->ns_unregister_filter(tex_filter);
 }
 
+/**
+ *
+ */
+INLINE TexturePool::LookupKey::
+LookupKey(Texture::TextureType texture_type,
+          int primary_file_num_channels, int alpha_file_channel,
+          const LoaderOptions &options, const SamplerState &sampler) :
+  _primary_file_num_channels(primary_file_num_channels),
+  _alpha_file_channel(alpha_file_channel),
+  _texture_type(texture_type),
+  _texture_format((Texture::Format)options.get_texture_format()),
+  _texture_compress((Texture::CompressionMode)options.get_texture_compression()),
+  _texture_quality((Texture::QualityLevel)options.get_texture_quality()),
+  _texture_sampler(sampler),
+  _force_srgb(options.get_texture_flags() & LoaderOptions::TF_force_srgb) {
+}
+
 /**
  * Defines relative ordering between LookupKey instances.
  */
 INLINE bool TexturePool::LookupKey::
 operator < (const LookupKey &other) const {
+  if (_texture_type != other._texture_type) {
+    return _texture_type < other._texture_type;
+  }
   if (_fullpath != other._fullpath) {
     return _fullpath < other._fullpath;
   }
@@ -350,5 +370,17 @@ operator < (const LookupKey &other) const {
   if (_alpha_file_channel != other._alpha_file_channel) {
     return _alpha_file_channel < other._alpha_file_channel;
   }
-  return _texture_type < other._texture_type;
+  if (_texture_format != other._texture_format) {
+    return _texture_format < other._texture_format;
+  }
+  if (_texture_compress != other._texture_compress) {
+    return _texture_compress < other._texture_compress;
+  }
+  if (_texture_quality != other._texture_quality) {
+    return _texture_quality < other._texture_quality;
+  }
+  if (_texture_sampler != other._texture_sampler) {
+    return _texture_sampler < other._texture_sampler;
+  }
+  return _force_srgb < other._force_srgb;
 }

+ 60 - 19
panda/src/gobj/texturePool.cxx

@@ -334,9 +334,9 @@ ns_get_texture(const Filename &orig_filename,
  */
 Texture *TexturePool::
 ns_load_texture(const Filename &orig_filename, int primary_file_num_channels,
-                bool read_mipmaps, const LoaderOptions &options) {
-  LookupKey key;
-  key._primary_file_num_channels = primary_file_num_channels;
+                bool read_mipmaps, const LoaderOptions &options, const SamplerState &sampler) {
+
+  LookupKey key(Texture::TT_2d_texture, primary_file_num_channels, 0, options, sampler);
   {
     MutexHolder holder(_lock);
     resolve_filename(key._fullpath, orig_filename, read_mipmaps, options);
@@ -484,7 +484,7 @@ ns_load_texture(const Filename &orig_filename, int primary_file_num_channels,
   if (use_filters) {
     tex = post_load(tex);
   }
-
+  apply_texture_attributes(tex, options, sampler);
   return tex;
 }
 
@@ -496,15 +496,13 @@ ns_load_texture(const Filename &orig_filename,
                 const Filename &orig_alpha_filename,
                 int primary_file_num_channels,
                 int alpha_file_channel,
-                bool read_mipmaps, const LoaderOptions &options) {
+                bool read_mipmaps, const LoaderOptions &options, const SamplerState &sampler) {
   if (!_fake_texture_image.empty()) {
     return ns_load_texture(_fake_texture_image, primary_file_num_channels,
-                           read_mipmaps, options);
+                           read_mipmaps, options, sampler);
   }
 
-  LookupKey key;
-  key._primary_file_num_channels = primary_file_num_channels;
-  key._alpha_file_channel = alpha_file_channel;
+  LookupKey key(Texture::TT_2d_texture, primary_file_num_channels, alpha_file_channel, options, sampler);
   {
     MutexHolder holder(_lock);
     resolve_filename(key._fullpath, orig_filename, read_mipmaps, options);
@@ -619,7 +617,7 @@ ns_load_texture(const Filename &orig_filename,
   if (use_filters) {
     tex = post_load(tex);
   }
-
+  apply_texture_attributes(tex, options, sampler);
   return tex;
 }
 
@@ -628,12 +626,11 @@ ns_load_texture(const Filename &orig_filename,
  */
 Texture *TexturePool::
 ns_load_3d_texture(const Filename &filename_pattern,
-                   bool read_mipmaps, const LoaderOptions &options) {
+                   bool read_mipmaps, const LoaderOptions &options, const SamplerState &sampler) {
   Filename orig_filename(filename_pattern);
   orig_filename.set_pattern(true);
 
-  LookupKey key;
-  key._texture_type = Texture::TT_3d_texture;
+  LookupKey key(Texture::TT_3d_texture, 0, 0, options, sampler);
   {
     MutexHolder holder(_lock);
     resolve_filename(key._fullpath, orig_filename, read_mipmaps, options);
@@ -722,6 +719,7 @@ ns_load_3d_texture(const Filename &filename_pattern,
   }
 
   nassertr(!tex->get_fullpath().empty(), tex);
+  apply_texture_attributes(tex, options, sampler);
   return tex;
 }
 
@@ -730,12 +728,11 @@ ns_load_3d_texture(const Filename &filename_pattern,
  */
 Texture *TexturePool::
 ns_load_2d_texture_array(const Filename &filename_pattern,
-                         bool read_mipmaps, const LoaderOptions &options) {
+                         bool read_mipmaps, const LoaderOptions &options, const SamplerState &sampler) {
   Filename orig_filename(filename_pattern);
   orig_filename.set_pattern(true);
 
-  LookupKey key;
-  key._texture_type = Texture::TT_2d_texture_array;
+  LookupKey key(Texture::TT_2d_texture_array, 0, 0, options, sampler);
   {
     MutexHolder holder(_lock);
     resolve_filename(key._fullpath, orig_filename, read_mipmaps, options);
@@ -824,6 +821,7 @@ ns_load_2d_texture_array(const Filename &filename_pattern,
   }
 
   nassertr(!tex->get_fullpath().empty(), tex);
+  apply_texture_attributes(tex, options, sampler);
   return tex;
 }
 
@@ -832,12 +830,11 @@ ns_load_2d_texture_array(const Filename &filename_pattern,
  */
 Texture *TexturePool::
 ns_load_cube_map(const Filename &filename_pattern, bool read_mipmaps,
-                 const LoaderOptions &options) {
+                 const LoaderOptions &options, const SamplerState &sampler) {
   Filename orig_filename(filename_pattern);
   orig_filename.set_pattern(true);
 
-  LookupKey key;
-  key._texture_type = Texture::TT_cube_map;
+  LookupKey key(Texture::TT_cube_map, 0, 0, options, sampler);
   {
     MutexHolder holder(_lock);
     resolve_filename(key._fullpath, orig_filename, read_mipmaps, options);
@@ -926,6 +923,7 @@ ns_load_cube_map(const Filename &filename_pattern, bool read_mipmaps,
   }
 
   nassertr(!tex->get_fullpath().empty(), tex);
+  apply_texture_attributes(tex, options, sampler);
   return tex;
 }
 
@@ -947,6 +945,49 @@ ns_get_normalization_cube_map(int size) {
   return _normalization_cube_map;
 }
 
+/**
+ * The texture is loaded, apply any atributes that were sent in with the texture through LoaderOptions
+ * and Sampler State
+ */
+void TexturePool::
+apply_texture_attributes(Texture *tex, const LoaderOptions &options, const SamplerState &sampler) {
+  int format = options.get_texture_format();
+  if (format != 0) {
+    tex->set_format((Texture::Format)format);
+  }
+  else if (options.get_texture_flags() & LoaderOptions::TF_force_srgb) {
+    int num_components = tex->get_num_components();
+    if (num_components == 1) {
+      if (!Texture::has_alpha(tex->get_format())) {
+        tex->set_format(Texture::F_sluminance);
+      }
+    }
+    else if (num_components == 2 && Texture::has_alpha(tex->get_format())) {
+      tex->set_format(Texture::F_sluminance_alpha);
+    }
+    else if (num_components == 3) {
+      tex->set_format(Texture::F_srgb);
+    }
+    else if (num_components == 4) {
+      tex->set_format(Texture::F_srgb_alpha);
+    }
+    else {
+      gobj_cat.warning()
+        << "Unable to enable sRGB format on texture " << tex->get_name()
+        << " with specified format " << tex->get_format() << "\n";
+    }
+  }
+  int compression = options.get_texture_compression();
+  if (compression != 0) {
+    tex->set_compression((Texture::CompressionMode)compression);
+  }
+  int quality = options.get_texture_quality();
+  if (quality != 0) {
+    tex->set_quality_level((Texture::QualityLevel)quality);
+  }
+  tex->set_default_sampler(sampler);
+}
+
 /**
  * The nonstatic implementation of get_alpha_scale_map().
  */

+ 35 - 10
panda/src/gobj/texturePool.h

@@ -49,22 +49,27 @@ PUBLISHED:
   BLOCKING INLINE static Texture *load_texture(const Filename &filename,
                                                int primary_file_num_channels = 0,
                                                bool read_mipmaps = false,
-                                               const LoaderOptions &options = LoaderOptions());
+                                               const LoaderOptions &options = LoaderOptions(),
+                                               const SamplerState &sampler = SamplerState());
   BLOCKING INLINE static Texture *load_texture(const Filename &filename,
                                                const Filename &alpha_filename,
                                                int primary_file_num_channels = 0,
                                                int alpha_file_channel = 0,
                                                bool read_mipmaps = false,
-                                               const LoaderOptions &options = LoaderOptions());
+                                               const LoaderOptions &options = LoaderOptions(),
+                                               const SamplerState &sampler = SamplerState());
   BLOCKING INLINE static Texture *load_3d_texture(const Filename &filename_pattern,
                                                   bool read_mipmaps = false,
-                                                  const LoaderOptions &options = LoaderOptions());
+                                                  const LoaderOptions &options = LoaderOptions(),
+                                                  const SamplerState &sampler = SamplerState());
   BLOCKING INLINE static Texture *load_2d_texture_array(const Filename &filename_pattern,
                                                         bool read_mipmaps = false,
-                                                        const LoaderOptions &options = LoaderOptions());
+                                                        const LoaderOptions &options = LoaderOptions(),
+                                                        const SamplerState &sampler = SamplerState());
   BLOCKING INLINE static Texture *load_cube_map(const Filename &filename_pattern,
                                                 bool read_mipmaps = false,
-                                                const LoaderOptions &options = LoaderOptions());
+                                                const LoaderOptions &options = LoaderOptions(),
+                                                const SamplerState &sampler = SamplerState());
 
   INLINE static Texture *get_normalization_cube_map(int size);
   INLINE static Texture *get_alpha_scale_map();
@@ -128,22 +133,31 @@ private:
   Texture *ns_load_texture(const Filename &orig_filename,
                            int primary_file_num_channels,
                            bool read_mipmaps,
-                           const LoaderOptions &options);
+                           const LoaderOptions &options,
+                           const SamplerState &sampler);
   Texture *ns_load_texture(const Filename &orig_filename,
                            const Filename &orig_alpha_filename,
                            int primary_file_num_channels,
                            int alpha_file_channel,
                            bool read_mipmaps,
-                           const LoaderOptions &options);
+                           const LoaderOptions &options,
+                           const SamplerState &sampler);
   Texture *ns_load_3d_texture(const Filename &filename_pattern,
                               bool read_mipmaps,
-                              const LoaderOptions &options);
+                              const LoaderOptions &options,
+                              const SamplerState &sampler);
   Texture *ns_load_2d_texture_array(const Filename &filename_pattern,
                                     bool read_mipmaps,
-                                    const LoaderOptions &options);
+                                    const LoaderOptions &options,
+                                    const SamplerState &sampler);
   Texture *ns_load_cube_map(const Filename &filename_pattern,
                             bool read_mipmaps,
-                            const LoaderOptions &options);
+                            const LoaderOptions &options,
+                            const SamplerState &sampler);
+
+  void apply_texture_attributes(Texture *tex, const LoaderOptions &options,
+                                const SamplerState &sampler);
+
   Texture *ns_get_normalization_cube_map(int size);
   Texture *ns_get_alpha_scale_map();
 
@@ -187,14 +201,25 @@ private:
   Mutex _filter_lock;
 
   struct LookupKey {
+    LookupKey() = default;
+    INLINE LookupKey(Texture::TextureType texture_type,
+                     int primary_file_num_channels, int alpha_file_channel,
+                     const LoaderOptions &options, const SamplerState &sampler);
+
     Filename _fullpath;
     Filename _alpha_fullpath;
     int _primary_file_num_channels = 0;
     int _alpha_file_channel = 0;
+    Texture::Format _texture_format = (Texture::Format)0;
+    Texture::QualityLevel _texture_quality = Texture::QL_default;
+    Texture::CompressionMode _texture_compress = Texture::CM_default;
+    SamplerState _texture_sampler;
     Texture::TextureType _texture_type = Texture::TT_2d_texture;
+    bool _force_srgb = false;
 
     INLINE bool operator < (const LookupKey &other) const;
   };
+
   typedef pmap<LookupKey, PT(Texture)> Textures;
   Textures _textures;
   typedef pmap<Filename, Filename> RelpathLookup;

+ 52 - 0
panda/src/putil/loaderOptions.I

@@ -18,6 +18,9 @@ constexpr LoaderOptions::
 LoaderOptions(int flags, int texture_flags) :
   _flags(flags),
   _texture_flags(texture_flags),
+  _texture_format(0),
+  _texture_compress(0),
+  _texture_quality(0),
   _texture_num_views(0),
   _auto_texture_scale(ATS_unspecified)
 {
@@ -55,6 +58,55 @@ get_texture_flags() const {
   return _texture_flags;
 }
 
+/**
+ * Set the texture format
+*/
+
+INLINE void LoaderOptions::
+set_texture_format(int format) {
+  _texture_format = format;
+}
+
+/**
+ *  Get the texture format
+ */
+INLINE int LoaderOptions::
+get_texture_format() const {
+  return _texture_format;
+}
+
+/**
+ *  Set the texture compression
+ */
+INLINE void LoaderOptions::
+set_texture_compression(int compress) {
+  _texture_compress = compress;
+}
+
+/**
+ *  Get the texture compression
+ */
+INLINE int LoaderOptions::
+get_texture_compression() const {
+  return _texture_compress;
+}
+
+/**
+ *  Set the texture quality
+ */
+INLINE void LoaderOptions::
+set_texture_quality(int quality) {
+  _texture_quality = quality;
+}
+
+/**
+ *  Get the texture quality
+ */
+INLINE int LoaderOptions::
+get_texture_quality() const {
+  return _texture_quality;
+}
+
 /**
  * Specifies the expected number of views to load for the texture.  This is
  * ignored unless TF_multiview is included in texture_flags.  This must be

+ 3 - 0
panda/src/putil/loaderOptions.cxx

@@ -24,6 +24,9 @@ LoaderOptions::
 LoaderOptions(int flags) :
   _flags(flags),
   _texture_flags(0),
+  _texture_format(0),
+  _texture_compress(0),
+  _texture_quality(0),
   _texture_num_views(0),
   _auto_texture_scale(ATS_unspecified)
 {

+ 11 - 0
panda/src/putil/loaderOptions.h

@@ -47,6 +47,7 @@ PUBLISHED:
     TF_float             = 0x0100,  // Load as a floating-point (depth) texture
     TF_allow_compression = 0x0200,  // Consider compressing RAM image
     TF_no_filters        = 0x0400,  // disallow using texture pool filters
+    TF_force_srgb        = 0x0800,  // Force the texture to have an sRGB format
   };
 
   explicit LoaderOptions(int flags = LF_search | LF_report_errors);
@@ -58,6 +59,13 @@ PUBLISHED:
 
   INLINE void set_texture_flags(int flags);
   INLINE int get_texture_flags() const;
+  INLINE void set_texture_format(int format);
+  INLINE int get_texture_format() const;
+  INLINE void set_texture_compression(int compress);
+  INLINE int get_texture_compression() const;
+  INLINE void set_texture_quality(int quality);
+  INLINE int get_texture_quality() const;
+
   INLINE void set_texture_num_views(int num_views);
   INLINE int get_texture_num_views() const;
   MAKE_PROPERTY(texture_flags, get_texture_flags, set_texture_flags);
@@ -78,6 +86,9 @@ private:
                           const std::string &flag_name, int flag) const;
   int _flags;
   int _texture_flags;
+  int _texture_format;
+  int _texture_compress;
+  int _texture_quality;
   int _texture_num_views;
   AutoTextureScale _auto_texture_scale;
 };