Search code examples
opengltexturesglsltexture-mappingtexture2d

glTexImage2D fails with error 1282 using PBO (bad texture resolution)


I have implemented Pixel Buffer Object (PBO) in my OpenGL application. However, I have the error 1282 when I try to load a texture using the function 'glTexImage2D'. It's very strange because the problems comes from textures with a specific resolution.

To have a better understanding of my problem let's examine 3 textures with 3 different resolutions:

a) blue.jpg Bpp: 24 Resolution: 259x469

enter image description here

b) green.jpg Bpp: 24 Resolution: 410x489

enter image description here

c) red.jpg Bpp: 24 Resolution: 640x480

enter image description here

Now let's examine the C++ code without PBO usage:

FIBITMAP *bitmap = FreeImage_Load(
    FreeImage_GetFIFFromFilename(file.GetFullName().c_str()), file.GetFullName().c_str());
FIBITMAP *pImage = FreeImage_ConvertTo32Bits(bitmap);

char *pPixels = (char*)FreeImage_GetBits(bitmap);
uint32_t width = FreeImage_GetWidth(bitmap);
uint32_t height = FreeImage_GetHeight(bitmap);
uint32_t byteSize = width * height * (FreeImage_GetBPP(bitmap)/8); //24 bits / 8 bits = 3 bytes)

glGenTextures(1, &this->m_Handle);
glBindTexture(GL_TEXTURE_2D, this->m_Handle);
{
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    std::cout << "ERROR: " << glGetError() << std::endl;

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width,
        height, 0, GL_BGR, GL_UNSIGNED_BYTE, pPixels);

    std::cout << "ERROR: " << glGetError() << std::endl;

    if (this->m_IsMipmap)
        glGenerateMipmap(this->m_Target);
}
glBindTexture(GL_TEXTURE_2D, 0);

For the 3 textures the output is always the same (so the loading has been executed correctly):

$> ERROR: 0
$> ERROR: 0

And the graphical result is also correct:

a) Blue

enter image description here

b) Green

enter image description here

c) Red

enter image description here

Now let's examine the C++ code using this time PBO:

FIBITMAP *bitmap = FreeImage_Load(
    FreeImage_GetFIFFromFilename(file.GetFullName().c_str()), file.GetFullName().c_str());
FIBITMAP *pImage = FreeImage_ConvertTo32Bits(bitmap);

char *pPixels = (char*)FreeImage_GetBits(bitmap);
uint32_t width = FreeImage_GetWidth(bitmap);
uint32_t height = FreeImage_GetHeight(bitmap);
uint32_t byteSize = width * height * (FreeImage_GetBPP(bitmap)/8);

uint32_t  pboID;

glGenTextures(1, &this->m_Handle);
glBindTexture(GL_TEXTURE_2D, this->m_Handle);
{
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    glGenBuffers(1, &pboID);
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboID);
    {
        unsigned int bufferSize = width * height * 3;

        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

        glBufferData(GL_PIXEL_UNPACK_BUFFER, bufferSize, 0, GL_STATIC_DRAW);
        glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, bufferSize, pPixels);

        std::cout << "ERROR: " << glGetError() << std::endl;

        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width,
            height, 0, GL_BGR, GL_UNSIGNED_BYTE, OFFSET_BUFFER(0));

        std::cout << "ERROR: " << glGetError() << std::endl;

        if (this->m_IsMipmap)
            glGenerateMipmap(this->m_Target);
    }
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
glBindTexture(GL_TEXTURE_2D, 0);

The output for blue.jpg (259x469) and green.jpg (410x489) is the following:

$> ERROR: 0
$> ERROR: 1282

The graphical output is of course both the same:

enter image description here

But now the most interesting if for the texture red.jpg (640x480) there is no error and the graphical output is correct:

enter image description here

So using the PBO method the 1282 error seems to refer to a texture resolution problem!

The OpenGL documentation says for the error 1282 (GL_INVALID_OPERATION) concerning PBO:

GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to the GL_PIXEL_UNPACK_BUFFER target and the buffer object's data store is currently mapped.

GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to the GL_PIXEL_UNPACK_BUFFER target and the data would be unpacked from the buffer object such that the memory reads required would exceed the data store size.

GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to the GL_PIXEL_UNPACK_BUFFER target and data is not evenly divisible into the number of bytes needed to store in memory a datum indicated by type.

But I don't understand what is wrong with my code implementation!

I thought maybe if I need to use PBO I'm allowed to load textures with a resolution with a multiple of 8... But I hope not!

UPDATE

I tried to add the line:

glPixelStorei(GL_UNPACK_ALIGNMENT, 1); //1: byte alignment

before the call of 'glTexImage2D'.

The error 1282 has disappeared but the display is not correct:

enter image description here

I'm really lost!

Does anyone can help me?


Solution

  • It is obvous that the image data you loaded is padded to generate a 4-byte alignment for each row. THis is what the GL expects by default, and what you most probably also used in your non-PBO case.

    When you switched to PBOs, you ignored that padding bytes per row, so your buffer was to small and the GL detected that aout-of -range access.

    When you finally switched to GL_UNPACK_ALIGNMENT of 1, there is no out-of-range access any more, and the error goes away. But you now lie about your data format. It is still padded, but you told the GL that it isn't. For the 640x480 image, the padding is zero bytes (as 640*3 is divisable by 4), but for the other two images, there are padding bytes at the end of each row.

    The correct solution is to leave GL_UNPACK_ALIGNMENT at the default of 4, and fix the calculation of bufferSize. You need to find out how many bytes there have to be added to each line so that the total bytes of the line are divisible by 4 again (that means, at most 3 bytes are added):

    unsigned int padding = ( 4 - (width * 3) % 4 ) % 4;
    

    Now, you can take these extra bytes into account, and get the final size of the buffer (and the image you have in memory):

    unsigned int bufferSize = (width *  3 + padding) * height;