I'm trying to upload GL_COMPRESSED_RGB_S3TC_DXT1_EXT format mipmaped texture levels using PBO.
the program is resizing and compressing the images using the stb_resize.h
and stb_dxt.h
libraries.
Apparently the image compression works okay, but uploading any mipmap levels produces following result:
However If I define no mipmap levels for the texture it renders correcly.
Also I'm not getting any errors from OpenGL as I have GL_KHR_debug
enabled.
Here is the opengl code for uploading mipmaps, with some of my own library code in between:
// Load image:
auto img = loadImage2D("parrot.png");
// Resize the image to power-of-two
int max_mipmaps = 1;
unsigned int max_size = nextPow2(std::max(img.width, img.height));
for(int i = max_size; i > 4; i >>= 1)
max_mipmaps++;
auto rimg = img.scaleCubic(max_size,max_size);
// Allocate PBO to source compressed mipmap levels from
handle_t pbo;
size_t pbosize = rimg.compressed_size();
glCreateBuffers(1, &pbo);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glBufferStorage(GL_PIXEL_UNPACK_BUFFER, pbosize, 0,
GL_DYNAMIC_STORAGE_BIT|GL_MAP_WRITE_BIT|GL_MAP_READ_BIT|GL_MAP_PERSISTENT_BIT);
// Map the buffer
void * pbodata = (char*)glMapBufferRange(
GL_PIXEL_UNPACK_BUFFER, 0, pbosize,
GL_MAP_WRITE_BIT|GL_MAP_READ_BIT
|GL_MAP_PERSISTENT_BIT
|GL_MAP_FLUSH_EXPLICIT_BIT);
// Compress the image and write the data directly into PBO.
// compress_to() fmt returns GL_COMPRESSED_RGB_S3TC_DXT1_EXT
option_t fmt;
rimg.compress_to(pbodata, fmt);
glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, pbosize);
// Allocate the texture
glCreateTextures(GL_TEXTURE_2D, 1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTextureStorage2D(texture, max_mipmaps, fmt, rimg.width, rimg.width);
// Upload base image level.
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glCompressedTextureSubImage2D(texture, 0, 0,0, rimg.width, rimg.height, fmt, rimg.compressed_size(), 0);
// Process and Upload mipmap levels.
unsigned int mipmapsize = max_size >> 1;
for(int i = 1; i < max_mipmaps && mipmapsize >= 4; ++i) {
// Resize next mipmap level.
rimg = img.scaleLinear(mipmapsize,mipmapsize);
// Compress the image and write result to *pbodata
rimg.compress_to(pbodata, fmt);
glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, pbosize);
// Upload mipmap image level.
glCompressedTexSubImage2D(GL_TEXTURE_2D, i, 0,0, rimg.width, rimg.height, fmt, rimg.compressed_size(), 0);
mipmapsize >>= 1;
}
// Discard the PBO
glUnmapNamedBuffer(pbo);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER,0);
glDeleteBuffers(1, &pbo);
// Set texture params.
glTextureParameteri(texture, GL_TEXTURE_BASE_LEVEL, 0);
glTextureParameteri(texture, GL_TEXTURE_MAX_LEVEL, max_mipmaps - 1);
glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
Doing glFlushMappedBufferRange
will not be sufficient to ensure proper synchronization with the GPU. And this is what seems to be happening here: the whole mipmap pyramid is created on the CPU, before the GPU processes the first glCompressedTexSubImage2D
call.
As a quick&dirty test for this hypothesis, you can add a glFinish
into the loop. To properly synchronize it, you could use GL Sync Objects, but it would be best if you avoided any need to synchronize at all: just use a PBO which is big enough to hold the whole mipmap pyramid data.