Asynchronous texture compression on CPU
This commit is contained in:
parent
58adeb01d1
commit
f0baefb6ff
4 changed files with 1256 additions and 7 deletions
|
@ -540,6 +540,10 @@ void Bed3D::render_prusa(const std::string &key, bool bottom) const
|
|||
}
|
||||
#endif // !ENABLE_COMPRESSED_TEXTURES
|
||||
}
|
||||
#if ENABLE_COMPRESSED_TEXTURES
|
||||
else if (m_texture.unsent_compressed_data_available())
|
||||
m_texture.send_compressed_data_to_gpu();
|
||||
#endif // ENABLE_COMPRESSED_TEXTURES
|
||||
|
||||
if (!bottom)
|
||||
{
|
||||
|
|
|
@ -12,6 +12,12 @@
|
|||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#if ENABLE_COMPRESSED_TEXTURES
|
||||
#include <thread>
|
||||
|
||||
#define STB_DXT_IMPLEMENTATION
|
||||
#include "stb_dxt/stb_dxt.h"
|
||||
#endif // ENABLE_COMPRESSED_TEXTURES
|
||||
|
||||
#include "nanosvg/nanosvg.h"
|
||||
#include "nanosvg/nanosvgrast.h"
|
||||
|
@ -21,6 +27,86 @@
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
#if ENABLE_COMPRESSED_TEXTURES
|
||||
void GLTexture::Compressor::reset()
|
||||
{
|
||||
// force compression completion, if any
|
||||
m_abort_compressing = true;
|
||||
// wait for compression completion, if any
|
||||
while (m_is_compressing) {}
|
||||
|
||||
m_levels.clear();
|
||||
}
|
||||
|
||||
void GLTexture::Compressor::add_level(unsigned int w, unsigned int h, const std::vector<unsigned char>& data)
|
||||
{
|
||||
m_levels.emplace_back(w, h, data);
|
||||
}
|
||||
|
||||
void GLTexture::Compressor::start_compressing()
|
||||
{
|
||||
m_is_compressing = true;
|
||||
m_abort_compressing = false;
|
||||
std::thread t(&GLTexture::Compressor::compress, this);
|
||||
t.detach();
|
||||
}
|
||||
|
||||
bool GLTexture::Compressor::unsent_compressed_data_available() const
|
||||
{
|
||||
for (const Level& level : m_levels)
|
||||
{
|
||||
if (!level.sent_to_gpu && (level.compressed_data.size() > 0))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GLTexture::Compressor::send_compressed_data_to_gpu()
|
||||
{
|
||||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_texture.m_id));
|
||||
for (int i = 0; i < (int)m_levels.size(); ++i)
|
||||
{
|
||||
Level& level = m_levels[i];
|
||||
if (!level.sent_to_gpu && (level.compressed_data.size() > 0))
|
||||
{
|
||||
glsafe(::glCompressedTexSubImage2D(GL_TEXTURE_2D, (GLint)i, 0, 0, (GLsizei)level.w, (GLsizei)level.h, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)level.compressed_data.size(), (const GLvoid*)level.compressed_data.data()));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, i));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (i > 0) ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR));
|
||||
level.sent_to_gpu = true;
|
||||
// we are done with the compressed data, we can discard it
|
||||
level.compressed_data.clear();
|
||||
}
|
||||
}
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
}
|
||||
|
||||
void GLTexture::Compressor::compress()
|
||||
{
|
||||
// reference: https://github.com/Cyan4973/RygsDXTc
|
||||
|
||||
for (Level& level : m_levels)
|
||||
{
|
||||
if (m_abort_compressing)
|
||||
break;
|
||||
|
||||
// stb_dxt library, despite claiming that the needed size of the destination buffer is equal to (source buffer size)/4,
|
||||
// crashes if doing so, so we start with twice the required size
|
||||
level.compressed_data = std::vector<unsigned char>(level.w * level.h * 2, 0);
|
||||
int compressed_size = 0;
|
||||
rygCompress(level.compressed_data.data(), level.src_data.data(), level.w, level.h, 1, compressed_size);
|
||||
level.compressed_data.resize(compressed_size);
|
||||
|
||||
// we are done with the source data, we can discard it
|
||||
level.src_data.clear();
|
||||
}
|
||||
|
||||
m_is_compressing = false;
|
||||
m_abort_compressing = false;
|
||||
}
|
||||
#endif // ENABLE_COMPRESSED_TEXTURES
|
||||
|
||||
GLTexture::Quad_UVs GLTexture::FullTextureUVs = { { 0.0f, 1.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f }, { 0.0f, 0.0f } };
|
||||
|
||||
GLTexture::GLTexture()
|
||||
|
@ -28,6 +114,9 @@ GLTexture::GLTexture()
|
|||
, m_width(0)
|
||||
, m_height(0)
|
||||
, m_source("")
|
||||
#if ENABLE_COMPRESSED_TEXTURES
|
||||
, m_compressor(*this)
|
||||
#endif // ENABLE_COMPRESSED_TEXTURES
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -249,8 +338,23 @@ void GLTexture::reset()
|
|||
m_width = 0;
|
||||
m_height = 0;
|
||||
m_source = "";
|
||||
#if ENABLE_COMPRESSED_TEXTURES
|
||||
m_compressor.reset();
|
||||
#endif // ENABLE_COMPRESSED_TEXTURES
|
||||
}
|
||||
|
||||
#if ENABLE_COMPRESSED_TEXTURES
|
||||
bool GLTexture::unsent_compressed_data_available() const
|
||||
{
|
||||
return m_compressor.unsent_compressed_data_available();
|
||||
}
|
||||
|
||||
void GLTexture::send_compressed_data_to_gpu()
|
||||
{
|
||||
m_compressor.send_compressed_data_to_gpu();
|
||||
}
|
||||
#endif // ENABLE_COMPRESSED_TEXTURES
|
||||
|
||||
void GLTexture::render_texture(unsigned int tex_id, float left, float right, float bottom, float top)
|
||||
{
|
||||
render_sub_texture(tex_id, left, right, bottom, top, FullTextureUVs);
|
||||
|
@ -416,6 +520,8 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
|
|||
bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, unsigned int max_size_px)
|
||||
#endif // ENABLE_COMPRESSED_TEXTURES
|
||||
{
|
||||
bool compression_enabled = compress && GLEW_EXT_texture_compression_s3tc;
|
||||
|
||||
NSVGimage* image = nsvgParseFromFile(filename.c_str(), "px", 96.0f);
|
||||
if (image == nullptr)
|
||||
{
|
||||
|
@ -428,6 +534,22 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, uns
|
|||
|
||||
m_width = (int)(scale * image->width);
|
||||
m_height = (int)(scale * image->height);
|
||||
|
||||
#if ENABLE_COMPRESSED_TEXTURES
|
||||
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;
|
||||
int height_rem = m_height % 4;
|
||||
|
||||
if (width_rem != 0)
|
||||
m_width += (4 - width_rem);
|
||||
|
||||
if (height_rem != 0)
|
||||
m_height += (4 - height_rem);
|
||||
}
|
||||
#endif // ENABLE_COMPRESSED_TEXTURES
|
||||
|
||||
int n_pixels = m_width * m_height;
|
||||
|
||||
if (n_pixels <= 0)
|
||||
|
@ -462,8 +584,13 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, uns
|
|||
glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
|
||||
}
|
||||
|
||||
if (compress && GLEW_EXT_texture_compression_s3tc)
|
||||
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()));
|
||||
if (compression_enabled)
|
||||
{
|
||||
// initializes the texture on GPU
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
|
||||
// and send the uncompressed data to the compressor
|
||||
m_compressor.add_level((unsigned int)m_width, (unsigned int)m_height, 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()));
|
||||
#else
|
||||
|
@ -475,7 +602,8 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, uns
|
|||
int lod_w = m_width;
|
||||
int lod_h = m_height;
|
||||
GLint level = 0;
|
||||
while ((lod_w > 1) || (lod_h > 1))
|
||||
// we do not need to generate all levels down to 1x1
|
||||
while ((lod_w > 64) || (lod_h > 64))
|
||||
{
|
||||
++level;
|
||||
|
||||
|
@ -483,10 +611,19 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, uns
|
|||
lod_h = std::max(lod_h / 2, 1);
|
||||
scale /= 2.0f;
|
||||
|
||||
#if ENABLE_COMPRESSED_TEXTURES
|
||||
data.resize(lod_w * lod_h * 4);
|
||||
#endif // ENABLE_COMPRESSED_TEXTURES
|
||||
|
||||
nsvgRasterize(rast, image, 0, 0, scale, data.data(), lod_w, lod_h, lod_w * 4);
|
||||
#if ENABLE_COMPRESSED_TEXTURES
|
||||
if (compress && GLEW_EXT_texture_compression_s3tc)
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
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));
|
||||
// and send the uncompressed data to the compressor
|
||||
m_compressor.add_level((unsigned int)lod_w, (unsigned int)lod_h, data);
|
||||
}
|
||||
else
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
#else
|
||||
|
@ -494,8 +631,15 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, uns
|
|||
#endif // ENABLE_COMPRESSED_TEXTURES
|
||||
}
|
||||
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
|
||||
#if ENABLE_COMPRESSED_TEXTURES
|
||||
if (!compression_enabled)
|
||||
{
|
||||
#endif // ENABLE_COMPRESSED_TEXTURES
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
|
||||
#if ENABLE_COMPRESSED_TEXTURES
|
||||
}
|
||||
#endif // ENABLE_COMPRESSED_TEXTURES
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -508,6 +652,12 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, uns
|
|||
|
||||
m_source = filename;
|
||||
|
||||
#if ENABLE_COMPRESSED_TEXTURES
|
||||
if (compression_enabled)
|
||||
// start asynchronous compression
|
||||
m_compressor.start_compressing();
|
||||
#endif // ENABLE_COMPRESSED_TEXTURES
|
||||
|
||||
nsvgDeleteRasterizer(rast);
|
||||
nsvgDelete(image);
|
||||
|
||||
|
|
|
@ -11,6 +11,42 @@ namespace GUI {
|
|||
|
||||
class GLTexture
|
||||
{
|
||||
#if ENABLE_COMPRESSED_TEXTURES
|
||||
class Compressor
|
||||
{
|
||||
struct Level
|
||||
{
|
||||
unsigned int w;
|
||||
unsigned int h;
|
||||
std::vector<unsigned char> src_data;
|
||||
std::vector<unsigned char> compressed_data;
|
||||
bool sent_to_gpu;
|
||||
|
||||
Level(unsigned int w, unsigned int h, const std::vector<unsigned char>& data) : w(w), h(h), src_data(data), sent_to_gpu(false) {}
|
||||
};
|
||||
|
||||
GLTexture& m_texture;
|
||||
std::vector<Level> m_levels;
|
||||
bool m_is_compressing;
|
||||
bool m_abort_compressing;
|
||||
|
||||
public:
|
||||
explicit Compressor(GLTexture& texture) : m_texture(texture), m_is_compressing(false), m_abort_compressing(false) {}
|
||||
|
||||
void reset();
|
||||
|
||||
void add_level(unsigned int w, unsigned int h, const std::vector<unsigned char>& data);
|
||||
|
||||
void start_compressing();
|
||||
|
||||
bool unsent_compressed_data_available() const;
|
||||
void send_compressed_data_to_gpu();
|
||||
|
||||
private:
|
||||
void compress();
|
||||
};
|
||||
#endif // ENABLE_COMPRESSED_TEXTURES
|
||||
|
||||
public:
|
||||
struct UV
|
||||
{
|
||||
|
@ -33,6 +69,9 @@ namespace GUI {
|
|||
int m_width;
|
||||
int m_height;
|
||||
std::string m_source;
|
||||
#if ENABLE_COMPRESSED_TEXTURES
|
||||
Compressor m_compressor;
|
||||
#endif // ENABLE_COMPRESSED_TEXTURES
|
||||
|
||||
public:
|
||||
GLTexture();
|
||||
|
@ -66,6 +105,11 @@ namespace GUI {
|
|||
|
||||
const std::string& get_source() const { return m_source; }
|
||||
|
||||
#if ENABLE_COMPRESSED_TEXTURES
|
||||
bool unsent_compressed_data_available() const;
|
||||
void send_compressed_data_to_gpu();
|
||||
#endif // ENABLE_COMPRESSED_TEXTURES
|
||||
|
||||
static void render_texture(unsigned int tex_id, float left, float right, float bottom, float top);
|
||||
static void render_sub_texture(unsigned int tex_id, float left, float right, float bottom, float top, const Quad_UVs& uvs);
|
||||
|
||||
|
@ -80,6 +124,8 @@ namespace GUI {
|
|||
#if ENABLE_COMPRESSED_TEXTURES
|
||||
bool load_from_png(const std::string& filename, bool use_mipmaps, bool compress);
|
||||
bool load_from_svg(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px);
|
||||
|
||||
friend class Compressor;
|
||||
#else
|
||||
bool load_from_png(const std::string& filename, bool use_mipmaps);
|
||||
bool load_from_svg(const std::string& filename, bool use_mipmaps, unsigned int max_size_px);
|
||||
|
|
1049
src/stb_dxt/stb_dxt.h
Normal file
1049
src/stb_dxt/stb_dxt.h
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue