Fixed generation of texture mipmap using glGenerateMipMap (added also for png)

Fix mipmap of compressed textures on AMD Radeon graphics cards by forcing the use of squared power of two textures

(cherry picked from commit prusa3d/PrusaSlicer@971f2a08e2)
(cherry picked from commit prusa3d/PrusaSlicer@eee4453993)
This commit is contained in:
enricoturri1966 2023-11-18 10:18:49 +08:00 committed by Noisyfox
parent c0386d786c
commit 8c0cf34a6c
6 changed files with 360 additions and 168 deletions

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2018 - 2023 Enrico Turri @enricoturri1966, Lukáš Hejl @hejllukas, Tomáš Mészáros @tamasmeszaros, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
//BBS:add i18n
#include "I18N.hpp"
//BBS: add fstream for debug output
@ -418,7 +422,7 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::stri
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
glsafe(::glGenTextures(1, &m_id));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_id));
if (compress && GLEW_EXT_texture_compression_s3tc)
if (compress && OpenGLManager::are_compressed_textures_supported())
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
else
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
@ -536,7 +540,7 @@ bool GLTexture::generate_from_text(const std::string &text_str, wxFont &font, wx
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
glsafe(::glGenTextures(1, &m_id));
glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id));
if (GLEW_EXT_texture_compression_s3tc)
if (OpenGLManager::are_compressed_textures_supported())
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
else
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
@ -637,7 +641,7 @@ bool GLTexture::generate_texture_from_text(const std::string& text_str, wxFont&
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
glsafe(::glGenTextures(1, &m_id));
glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id));
if (GLEW_EXT_texture_compression_s3tc)
if (OpenGLManager::are_compressed_textures_supported())
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
else
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
@ -677,9 +681,29 @@ void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right,
glsafe(::glDisable(GL_BLEND));
}
static bool to_squared_power_of_two(const std::string& filename, int max_size_px, int& w, int& h)
{
auto is_power_of_two = [](int v) { return v != 0 && (v & (v - 1)) == 0; };
auto upper_power_of_two = [](int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; };
int new_w = std::max(w, h);
if (!is_power_of_two(new_w))
new_w = upper_power_of_two(new_w);
while (new_w > max_size_px) {
new_w /= 2;
}
const int new_h = new_w;
const bool ret = (new_w != w || new_h != h);
w = new_w;
h = new_h;
return ret;
}
bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy)
{
bool compression_enabled = (compression_type != None) && GLEW_EXT_texture_compression_s3tc;
const bool compression_enabled = (compression_type != None) && OpenGLManager::are_compressed_textures_supported();
// Load a PNG with an alpha channel.
wxImage image;
@ -693,6 +717,11 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo
bool requires_rescale = false;
if (use_mipmaps && compression_enabled && OpenGLManager::force_power_of_two_textures()) {
if (to_squared_power_of_two(boost::filesystem::path(filename).filename().string(), OpenGLManager::get_gl_info().get_max_tex_size(), m_width, m_height))
requires_rescale = true;
}
if (compression_enabled && compression_type == MultiThreaded) {
// the stb_dxt compression library seems to like only texture sizes which are a multiple of 4
int width_rem = m_width % 4;
@ -819,7 +848,7 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo
m_source = filename;
if (compression_enabled && compression_type == MultiThreaded)
if (compression_type == MultiThreaded)
// start asynchronous compression
m_compressor.start_compressing();
@ -828,7 +857,7 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo
bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px)
{
bool compression_enabled = compress && GLEW_EXT_texture_compression_s3tc;
const bool compression_enabled = compress && OpenGLManager::are_compressed_textures_supported();
NSVGimage* image = nsvgParseFromFile(filename.c_str(), "px", 96.0f);
if (image == nullptr) {
@ -836,11 +865,17 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
return false;
}
float scale = (float)max_size_px / std::max(image->width, image->height);
const float scale = (float)max_size_px / std::max(image->width, image->height);
m_width = (int)(scale * image->width);
m_height = (int)(scale * image->height);
if (use_mipmaps && compression_enabled && OpenGLManager::force_power_of_two_textures())
to_squared_power_of_two(boost::filesystem::path(filename).filename().string(), max_size_px, m_width, m_height);
float scale_w = (float)m_width / image->width;
float scale_h = (float)m_height / image->height;
if (compression_enabled) {
// the stb_dxt compression library seems to like only texture sizes which are a multiple of 4
int width_rem = m_width % 4;
@ -853,7 +888,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
m_height += (4 - height_rem);
}
int n_pixels = m_width * m_height;
const int n_pixels = m_width * m_height;
if (n_pixels <= 0) {
reset();
@ -870,7 +905,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
// creates the temporary buffer only once, with max size, and reuse it for all the levels, if generating mipmaps
std::vector<unsigned char> data(n_pixels * 4, 0);
nsvgRasterize(rast, image, 0, 0, scale, data.data(), m_width, m_height, m_width * 4);
nsvgRasterizeXY(rast, image, 0, 0, scale_w, scale_h, data.data(), m_width, m_height, m_width * 4);
// sends data to gpu
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
@ -892,7 +927,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
else
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
if (use_mipmaps && OpenGLManager::use_manually_generated_mipmaps()) {
if (use_mipmaps) {
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
int lod_w = m_width;
int lod_h = m_height;
@ -902,11 +937,12 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
lod_w = std::max(lod_w / 2, 1);
lod_h = std::max(lod_h / 2, 1);
scale /= 2.0f;
scale_w /= 2.0f;
scale_h /= 2.0f;
data.resize(lod_w * lod_h * 4);
nsvgRasterize(rast, image, 0, 0, scale, data.data(), lod_w, lod_h, lod_w * 4);
nsvgRasterizeXY(rast, image, 0, 0, scale_w, scale_h, data.data(), lod_w, lod_h, lod_w * 4);
if (compression_enabled) {
// initializes the texture on GPU
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
@ -921,9 +957,8 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
}
} else if (use_mipmaps && !OpenGLManager::use_manually_generated_mipmaps()) {
glGenerateMipmap(GL_TEXTURE_2D);
} else {
}
else {
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
}