better fixes for auto-compressed textures

This commit is contained in:
David Rose 2009-01-16 18:01:35 +00:00
parent 2f47cbbb17
commit 32581604cf
12 changed files with 271 additions and 156 deletions

View File

@ -196,14 +196,6 @@ ConfigVariableString red_blue_stereo_colors
"be a two-word string, where each word is one of 'red', 'blue', "
"'green', or 'alpha'."));
ConfigVariableBool auto_generate_mipmaps
("auto-generate-mipmaps", false,
PRC_DESC("Set this true to use the hardware to generate mipmaps "
"automatically in all cases, if supported. Set it false "
"to generate mipmaps in software when possible. This is "
"false by default because some drivers (Intel) seem to do a "
"poor job of generating mipmaps when needed."));
ConfigVariableBool color_scale_via_lighting
("color-scale-via-lighting", true,
PRC_DESC("When this is true, Panda will try to implement ColorAttribs and "

View File

@ -57,7 +57,6 @@ extern EXPCL_PANDA_DISPLAY ConfigVariableBool copy_texture_inverted;
extern EXPCL_PANDA_DISPLAY ConfigVariableBool window_inverted;
extern EXPCL_PANDA_DISPLAY ConfigVariableBool red_blue_stereo;
extern EXPCL_PANDA_DISPLAY ConfigVariableString red_blue_stereo_colors;
extern EXPCL_PANDA_DISPLAY ConfigVariableBool auto_generate_mipmaps;
extern EXPCL_PANDA_DISPLAY ConfigVariableBool color_scale_via_lighting;
extern EXPCL_PANDA_DISPLAY ConfigVariableBool alpha_scale_via_texture;
extern EXPCL_PANDA_DISPLAY ConfigVariableBool allow_incomplete_render;

View File

@ -934,6 +934,21 @@ load_texture(TextureDef &def, EggTexture *egg_tex) {
options.set_texture_flags(options.get_texture_flags() | LoaderOptions::TF_preload_simple);
}
if (!egg_ignore_filters && !egg_ignore_mipmaps) {
switch (egg_tex->get_minfilter()) {
case EggTexture::FT_nearest:
case EggTexture::FT_linear:
case EggTexture::FT_unspecified:
break;
case EggTexture::FT_nearest_mipmap_nearest:
case EggTexture::FT_linear_mipmap_nearest:
case EggTexture::FT_nearest_mipmap_linear:
case EggTexture::FT_linear_mipmap_linear:
options.set_texture_flags(options.get_texture_flags() | LoaderOptions::TF_generate_mipmaps);
}
}
PT(Texture) tex;
switch (egg_tex->get_texture_type()) {
case EggTexture::TT_unspecified:

View File

@ -660,11 +660,6 @@ reset() {
_supports_generate_mipmap =
has_extension("GL_SGIS_generate_mipmap") || is_at_least_version(1, 4);
// Temporary hack. There is an issue with auto-generating mipmaps
// for pre-compressed images, on certain drivers. Until I check in
// a fix, let's turn off this feature in general.
_supports_generate_mipmap = false;
_supports_multitexture = false;
_supports_mesa_6 = false;
@ -7278,9 +7273,12 @@ upload_texture_image(CLP(TextureContext) *gtc,
if (num_ram_mipmap_levels == 1) {
// No RAM mipmap levels available. Should we generate some?
if (!_supports_generate_mipmap ||
(!auto_generate_mipmaps && image_compression == Texture::CM_off)) {
// Yes, the GL won't generate them, so we need to.
if (!_supports_generate_mipmap || !driver_generate_mipmaps ||
image_compression != Texture::CM_off) {
// Yes, the GL can't or won't generate them, so we need to.
// Note that some drivers (nVidia) will *corrupt memory* if
// you ask them to generate mipmaps for a pre-compressed
// texture.
tex->generate_ram_mipmap_images();
num_ram_mipmap_levels = tex->get_num_ram_mipmap_images();
}

View File

@ -125,6 +125,15 @@ ConfigVariableBool driver_compress_textures
"and will always be handed to the graphics driver, regardless "
"of this setting."));
ConfigVariableBool driver_generate_mipmaps
("driver-generate-mipmaps", false,
PRC_DESC("Set this true to use the hardware to generate mipmaps "
"automatically in all cases, if supported. Set it false "
"to generate mipmaps in software when possible. This is "
"false by default because some drivers (Intel) seem to do a "
"poor job of generating mipmaps when needed; also, generating "
"mipmaps in software may allow smoother texture loads."));
ConfigVariableBool vertex_buffers
("vertex-buffers", true,
PRC_DESC("Set this true to allow the use of vertex buffers (or buffer "

View File

@ -56,6 +56,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 driver_compress_textures;
extern EXPCL_PANDA_GOBJ ConfigVariableBool driver_generate_mipmaps;
extern EXPCL_PANDA_GOBJ ConfigVariableBool vertex_buffers;
extern EXPCL_PANDA_GOBJ ConfigVariableBool vertex_arrays;
extern EXPCL_PANDA_GOBJ ConfigVariableBool display_lists;

View File

@ -254,7 +254,8 @@ load(const PNMImage &pnmimage, const LoaderOptions &options) {
++_properties_modified;
++_image_modified;
if (do_load_one(pnmimage, get_name(), 0, 0, options)) {
consider_auto_compress_ram_image();
bool generate_mipmaps = ((options.get_texture_flags() & LoaderOptions::TF_generate_mipmaps) != 0);
consider_auto_process_ram_image(generate_mipmaps || uses_mipmaps(), true);
return true;
}
return false;
@ -272,7 +273,6 @@ load(const PNMImage &pnmimage, int z, int n, const LoaderOptions &options) {
++_properties_modified;
++_image_modified;
if (do_load_one(pnmimage, get_name(), z, n, options)) {
consider_auto_compress_ram_image();
return true;
}
return false;
@ -1296,6 +1296,19 @@ has_ram_mipmap_image(int n) const {
return do_has_ram_mipmap_image(n);
}
////////////////////////////////////////////////////////////////////
// Function: Texture::has_all_ram_mipmap_images
// Access: Published
// Description: Returns true if all expected mipmap levels have been
// defined and exist in the system RAM, or false if even
// one mipmap level is missing.
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
has_all_ram_mipmap_images() const {
MutexHolder holder(_lock);
return do_has_all_ram_mipmap_images();
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_ram_mipmap_image_size
// Access: Published
@ -1403,6 +1416,26 @@ clear_ram_mipmap_images() {
do_clear_ram_mipmap_images();
}
////////////////////////////////////////////////////////////////////
// Function: Texture::generate_ram_mipmap_images
// Access: Published
// Description: Automatically fills in the n mipmap levels of the
// Texture, based on the texture's source image. This
// requires the texture's uncompressed ram image to be
// available in system memory. If it is not already, it
// will be fetched if possible.
//
// This call is not normally necessary, since the mipmap
// levels will be generated automatically if needed.
// But there may be certain cases in which you would
// like to call this explicitly.
////////////////////////////////////////////////////////////////////
INLINE void Texture::
generate_ram_mipmap_images() {
MutexHolder holder(_lock);
do_generate_ram_mipmap_images();
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_simple_x_size
// Access: Published

View File

@ -859,42 +859,6 @@ get_num_loadable_ram_mipmap_images() const {
return n;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::has_all_ram_mipmap_images
// Access: Published
// Description: Returns true if all expected mipmap levels have been
// defined and exist in the system RAM, or false if even
// one mipmap level is missing.
////////////////////////////////////////////////////////////////////
bool Texture::
has_all_ram_mipmap_images() const {
MutexHolder holder(_lock);
if (_ram_images.empty() || _ram_images[0]._image.empty()) {
// If we don't even have a base image, the answer is no.
return false;
}
if (!is_mipmap(_minfilter)) {
// If we have a base image and don't require mipmapping, the
// answer is yes.
return true;
}
// Check that we have enough mipmap levels to meet the size
// requirements.
int size = max(_x_size, max(_y_size, _z_size));
int n = 0;
int x = 1;
while (x < size) {
x = (x << 1);
++n;
if (n >= (int)_ram_images.size() || _ram_images[n]._image.empty()) {
return false;
}
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_ram_mipmap_image
// Access: Published
@ -959,67 +923,6 @@ clear_ram_mipmap_image(int n) {
_ram_images[n]._page_size = 0;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::generate_ram_mipmap_images
// Access: Published
// Description: Automatically fills in the n mipmap levels of the
// Texture, based on the texture's source image. This
// requires the texture's uncompressed ram image to be
// available in system memory. If it is not already, it
// will be fetched if possible.
//
// This call is not normally necessary, since the mipmap
// levels will be generated automatically if needed.
// But there may be certain cases in which you would
// like to call this explicitly.
////////////////////////////////////////////////////////////////////
void Texture::
generate_ram_mipmap_images() {
MutexHolder holder(_lock);
do_get_uncompressed_ram_image();
nassertv(do_has_ram_image());
nassertv(_ram_image_compression == CM_off);
nassertv(_component_type != T_float);
do_clear_ram_mipmap_images();
if (gobj_cat.is_debug()) {
gobj_cat.debug()
<< "Generating mipmap levels for " << *this << "\n";
}
if (_texture_type == Texture::TT_3d_texture && _z_size != 1) {
// Eek, a 3-D texture.
int x_size = _x_size;
int y_size = _y_size;
int z_size = _z_size;
int n = 0;
while (x_size > 1 || y_size > 1 || z_size > 1) {
_ram_images.push_back(RamImage());
filter_3d_mipmap_level(_ram_images[n + 1], _ram_images[n],
x_size, y_size, z_size);
x_size = max(x_size >> 1, 1);
y_size = max(y_size >> 1, 1);
z_size = max(z_size >> 1, 1);
++n;
}
} else {
// A 1-D, 2-D, or cube map texture.
int x_size = _x_size;
int y_size = _y_size;
int n = 0;
while (x_size > 1 || y_size > 1) {
_ram_images.push_back(RamImage());
filter_2d_mipmap_pages(_ram_images[n + 1], _ram_images[n],
x_size, y_size);
x_size = max(x_size >> 1, 1);
y_size = max(y_size >> 1, 1);
++n;
}
}
}
////////////////////////////////////////////////////////////////////
// Function: Texture::modify_simple_ram_image
// Access: Published
@ -2097,8 +2000,9 @@ do_read(const Filename &fullpath, const Filename &alpha_fullpath,
} 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();
// compressing it etc.
bool generate_mipmaps = ((options.get_texture_flags() & LoaderOptions::TF_generate_mipmaps) != 0);
consider_auto_process_ram_image(generate_mipmaps || uses_mipmaps(), true);
}
}
@ -3029,8 +2933,11 @@ do_reload_ram_image(bool allow_compression) {
_ram_images = tex->_ram_images;
_loaded_from_image = true;
if (allow_compression && consider_auto_compress_ram_image()) {
if (cache->get_cache_compressed_textures()) {
bool was_compressed = (_ram_image_compression != CM_off);
if (consider_auto_process_ram_image(uses_mipmaps(), allow_compression)) {
bool is_compressed = (_ram_image_compression != CM_off);
if (!was_compressed && is_compressed &&
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.
@ -3153,38 +3060,44 @@ do_make_ram_mipmap_image(int n) {
}
////////////////////////////////////////////////////////////////////
// Function: Texture::consider_auto_compress_ram_image
// Function: Texture::consider_auto_process_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.
// RAM, this considers generating mipmaps and/or
// compressing the RAM image.
//
// Returns true if the image was modified by this
// operation, false if it wasn't.
////////////////////////////////////////////////////////////////////
bool Texture::
consider_auto_compress_ram_image() {
if (!driver_compress_textures) {
consider_auto_process_ram_image(bool generate_mipmaps, bool allow_compression) {
bool modified = false;
if (generate_mipmaps && !driver_generate_mipmaps &&
_ram_images.size() == 1) {
do_generate_ram_mipmap_images();
modified = true;
}
if (allow_compression && !driver_compress_textures) {
CompressionMode compression = _compression;
if (compression == CM_default) {
if (!compressed_textures) {
return false;
}
if (compression == CM_default && compressed_textures) {
compression = CM_on;
}
if (compression != CM_off && _ram_image_compression == CM_off) {
GraphicsStateGuardianBase *gsg = GraphicsStateGuardianBase::get_default_gsg();
if (do_compress_ram_image(compression, QL_default, gsg)) {
gobj_cat.info()
<< "Compressed " << get_name() << " with "
<< _ram_image_compression << "\n";
return true;
if (gobj_cat.is_debug()) {
gobj_cat.debug()
<< "Compressed " << get_name() << " with "
<< _ram_image_compression << "\n";
}
modified = true;
}
}
}
return false;
return modified;
}
////////////////////////////////////////////////////////////////////
@ -3324,6 +3237,39 @@ do_uncompress_ram_image() {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::do_has_all_ram_mipmap_images
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
bool Texture::
do_has_all_ram_mipmap_images() const {
if (_ram_images.empty() || _ram_images[0]._image.empty()) {
// If we don't even have a base image, the answer is no.
return false;
}
if (!is_mipmap(_minfilter)) {
// If we have a base image and don't require mipmapping, the
// answer is yes.
return true;
}
// Check that we have enough mipmap levels to meet the size
// requirements.
int size = max(_x_size, max(_y_size, _z_size));
int n = 0;
int x = 1;
while (x < size) {
x = (x << 1);
++n;
if (n >= (int)_ram_images.size() || _ram_images[n]._image.empty()) {
return false;
}
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::do_reconsider_z_size
// Access: Protected
@ -3866,8 +3812,10 @@ do_get_uncompressed_ram_image() {
// 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";
if (gobj_cat.is_debug()) {
gobj_cat.debug()
<< "Uncompressed " << get_name() << "\n";
}
return _ram_images[0]._image;
}
}
@ -4172,6 +4120,103 @@ do_clear_ram_mipmap_images() {
}
}
////////////////////////////////////////////////////////////////////
// Function: Texture::do_generate_ram_mipmap_images
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
void Texture::
do_generate_ram_mipmap_images() {
nassertv(do_has_ram_image());
nassertv(_component_type != T_float);
if (do_get_expected_num_mipmap_levels() == 1) {
// Don't bother.
return;
}
RamImage orig_compressed_image;
CompressionMode orig_compression_mode = CM_off;
if (_ram_image_compression != CM_off) {
// The RAM image is compressed. This means we need to uncompress
// it in order to generate mipmap images. Save the original
// first, to avoid lossy recompression.
orig_compressed_image = _ram_images[0];
orig_compression_mode = _ram_image_compression;
// Now try to get the uncompressed source image.
do_get_uncompressed_ram_image();
nassertv(_ram_image_compression == CM_off);
}
do_clear_ram_mipmap_images();
if (gobj_cat.is_debug()) {
gobj_cat.debug()
<< "Generating mipmap levels for " << *this << "\n";
}
if (_texture_type == Texture::TT_3d_texture && _z_size != 1) {
// Eek, a 3-D texture.
int x_size = _x_size;
int y_size = _y_size;
int z_size = _z_size;
int n = 0;
while (x_size > 1 || y_size > 1 || z_size > 1) {
_ram_images.push_back(RamImage());
filter_3d_mipmap_level(_ram_images[n + 1], _ram_images[n],
x_size, y_size, z_size);
x_size = max(x_size >> 1, 1);
y_size = max(y_size >> 1, 1);
z_size = max(z_size >> 1, 1);
++n;
}
} else {
// A 1-D, 2-D, or cube map texture.
int x_size = _x_size;
int y_size = _y_size;
int n = 0;
while (x_size > 1 || y_size > 1) {
_ram_images.push_back(RamImage());
filter_2d_mipmap_pages(_ram_images[n + 1], _ram_images[n],
x_size, y_size);
x_size = max(x_size >> 1, 1);
y_size = max(y_size >> 1, 1);
++n;
}
}
if (orig_compression_mode != CM_off) {
// Now attempt to recompress the mipmap images according to the
// original compression mode. We don't need to bother compressing
// the first image (it was already compressed, after all), so
// temporarily remove it from the top of the mipmap stack, and
// compress all of the rest of them instead.
nassertv(_ram_images.size() > 1);
int l0_x_size = _x_size;
int l0_y_size = _y_size;
int l0_z_size = _z_size;
_x_size = do_get_expected_mipmap_x_size(1);
_y_size = do_get_expected_mipmap_y_size(1);
_z_size = do_get_expected_mipmap_z_size(1);
RamImage uncompressed_image = _ram_images[0];
_ram_images.erase(_ram_images.begin());
bool success = do_compress_ram_image(orig_compression_mode, QL_default, NULL);
// Now restore the toplevel image.
if (success) {
_ram_images.insert(_ram_images.begin(), orig_compressed_image);
} else {
_ram_images.insert(_ram_images.begin(), uncompressed_image);
}
_x_size = l0_x_size;
_y_size = l0_y_size;
_z_size = l0_z_size;
}
}
////////////////////////////////////////////////////////////////////
// Function: Texture::do_set_pad_size
// Access: Protected
@ -5328,6 +5373,13 @@ do_squish(Texture::CompressionMode compression, int squish_flags) {
if (_ram_images.empty()) {
return false;
}
if (!do_has_all_ram_mipmap_images()) {
// If we're about to compress the RAM image, we should ensure that
// we have all of the mipmap levels first.
do_generate_ram_mipmap_images();
}
RamImages compressed_ram_images;
compressed_ram_images.reserve(_ram_images.size());
for (size_t n = 0; n < _ram_images.size(); ++n) {
@ -5562,28 +5614,31 @@ make_from_bam(const FactoryParams &params) {
}
}
LoaderOptions options = manager->get_loader_options();
if (false) { // temporary hack standin
options.set_texture_flags(options.get_texture_flags() | LoaderOptions::TF_generate_mipmaps);
}
switch (texture_type) {
case TT_1d_texture:
case TT_2d_texture:
if (alpha_filename.empty()) {
me = TexturePool::load_texture(filename, primary_file_num_channels,
false, manager->get_loader_options());
false, options);
} else {
me = TexturePool::load_texture(filename, alpha_filename,
primary_file_num_channels,
alpha_file_channel,
false, manager->get_loader_options());
false, options);
}
break;
case TT_3d_texture:
me = TexturePool::load_3d_texture(filename, false,
manager->get_loader_options());
me = TexturePool::load_3d_texture(filename, false, options);
break;
case TT_cube_map:
me = TexturePool::load_cube_map(filename, false,
manager->get_loader_options());
me = TexturePool::load_cube_map(filename, false, options);
break;
}
}
@ -5822,7 +5877,7 @@ write_datagram(BamWriter *manager, Datagram &me) {
me.add_string(alpha_filename);
me.add_uint8(_primary_file_num_channels);
me.add_uint8(_alpha_file_channel);
me.add_uint8(has_rawdata);
me.add_bool(has_rawdata);
me.add_uint8(_texture_type);
// The data beginning at this point is handled by fillin().

View File

@ -321,7 +321,7 @@ PUBLISHED:
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;
bool has_all_ram_mipmap_images() const;
INLINE bool has_all_ram_mipmap_images() const;
INLINE size_t get_ram_mipmap_image_size(int n) const;
INLINE size_t get_ram_mipmap_page_size(int n) const;
INLINE size_t get_expected_ram_mipmap_image_size(int n) const;
@ -332,7 +332,7 @@ PUBLISHED:
void set_ram_mipmap_image(int n, PTA_uchar image, size_t page_size = 0);
void clear_ram_mipmap_image(int n);
INLINE void clear_ram_mipmap_images();
void generate_ram_mipmap_images();
INLINE void generate_ram_mipmap_images();
INLINE int get_simple_x_size() const;
INLINE int get_simple_y_size() const;
@ -480,11 +480,13 @@ 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 consider_auto_process_ram_image(bool generate_mipmaps,
bool allow_compression);
bool do_compress_ram_image(CompressionMode compression,
QualityLevel quality_level,
GraphicsStateGuardianBase *gsg);
bool do_uncompress_ram_image();
bool do_has_all_ram_mipmap_images() const;
bool do_reconsider_z_size(int z);
bool do_reconsider_image_properties(int x_size, int y_size, int num_components,
@ -533,6 +535,7 @@ protected:
INLINE void do_clear_ram_image();
void do_clear_simple_ram_image();
void do_clear_ram_mipmap_images();
void do_generate_ram_mipmap_images();
void do_set_pad_size(int x, int y, int z);
// This nested class declaration is used below.

View File

@ -921,14 +921,20 @@ try_load_cache(PT(Texture) &tex, BamCache *cache, const Filename &filename,
// 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;
} else {
bool was_compressed = (tex->get_ram_image_compression() != Texture::CM_off);
if (tex->consider_auto_process_ram_image(tex->uses_mipmaps(), true)) {
bool is_compressed = (tex->get_ram_image_compression() != Texture::CM_off);
if (!was_compressed && is_compressed &&
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);

View File

@ -57,6 +57,7 @@ output(ostream &out) const {
write_flag(out, sep, "LF_no_disk_cache", LF_no_disk_cache);
write_flag(out, sep, "LF_no_ram_cache", LF_no_ram_cache);
}
write_flag(out, sep, "LF_allow_instance", LF_allow_instance);
if (sep.empty()) {
out << "0";
}
@ -66,6 +67,8 @@ output(ostream &out) const {
sep = "";
write_texture_flag(out, sep, "TF_preload", TF_preload);
write_texture_flag(out, sep, "TF_preload_simple", TF_preload_simple);
write_texture_flag(out, sep, "TF_allow_1d", TF_allow_1d);
write_texture_flag(out, sep, "TF_generate_mipmaps", TF_generate_mipmaps);
if (sep.empty()) {
out << "0";
}

View File

@ -43,6 +43,7 @@ PUBLISHED:
TF_preload = 0x0004, // Texture will have RAM image
TF_preload_simple = 0x0008, // Texture will have simple RAM image
TF_allow_1d = 0x0010, // If texture is Nx1, make a 1-d texture
TF_generate_mipmaps = 0x0020, // Consider generating mipmaps
};
LoaderOptions(int flags = LF_search | LF_report_errors);