invalid operation on PBOs

Hello all,

I have 3 PBOs for the Y, U, and V data of a video decoder. I’m converting these to RGB in a frag shader and using it as my frag color.

I’m using this:
#define PBO_BUFFER_OFFSET(i) ((char *)NULL + (i))

in order to pass the tex data to glTexSubImage2D.
It turns out, although this works when i=0, it throws an invalid op (1282) for i=1 and i=2.

The only difference I’ve seen in my code, and other code on the web that uses PBOs, is that mine uses LUMINANCE textures with GL_UNSIGNED_BYTE (normaly ppl have RGBs and GL_FLOAT).
I’m guessing that would mess up the offseting (???)

Anyway, here’s the code (for the Y data…it is identical for U and V data, with the PBO_BUFFER_OFFSET being 1 and 2 respectively. Obviously U ans V data is half the width and height, but that doesn’t really matter here).

// Y on tex unit 0
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mTexID_YUV[0]);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

// we won’t have new data from the decoder every tick. just bind the old texture if we don’t have new data
if (mHasNewTexData)
{
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mBufID_YUV[0]);
glBufferData(GL_PIXEL_UNPACK_BUFFER, widthheightsizeof(unsigned char), 0, GL_STREAM_DRAW);
void* memChunkY = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
if (memChunkY != 0)
{
memcpy(memChunkY, decoder_data.y, widthheightsizeof(unsigned char));
}
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, PBO_BUFFER_OFFSET(0));
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}

int sampler2D1 = glGetUniformLocation(mShaderProgram, “texY”);
glUniform1i(sampler2D1, 0);

Any ideas what could be wrong?
cheers,
g.

Which call exactly throws the invalid operation? The glTexSubImage2D?

Anyway, here’s the code (for the Y data…it is identical for U and V data, with the PBO_BUFFER_OFFSET being 1 and 2 respectively.
How should that work with #define PBO_BUFFER_OFFSET(i) ((char *)NULL + (i))?

You put the data into a PBO with tight alignment and do a TexSubImage2D from that.
There’s no interleave as PBO_BUFFER_OFFSET(1) and PBO_BUFFER_OFFSET(2) would indicate.
(Or you didn’t post enough code.)

In that case the PBO wouldn’t be big enough for offsets 1 and 2 and throw the error.
If you have three PBOs, all should start at offset 0.
The only thing which would need to iterate is mTexID_YUV[i].

Invalid op can be because app pass odd offset (1). Maybe hw require address with 4byte padding (dividable by 4).

If you want to convert YUYV to RGB you can think of another solution… pass YUYV macro-pixel as half-X resolution RGBA texture and use following shader:

// NOTE:
// video is 720x576, But texture is 360x576. Render quad that stretch 
// texture 2x on X axis, so pixels should be doubled twice.
// this texture should have nearest filtering

// yuyv is y1-u-y2-v -> two pixels y1uv and y2uv
// if we map y1-u-y2-v to col.rgba -> y1 = r, y2 = b, u = g, v = a
uniform sampler2D yuyv;

// luminance texture 2x1 -> it is 01. tile it accross whole yuyv texture... 
// it'a verical bars 1 pixel wide
// 0101010101010101010.. 
// 0101010101010101010.. 
// 0101010101010101010.. 
// 0101010101010101010.. 
// this texture should have nearest filtering
uniform sampler2D mask; 

void main(void)
{
 vec4 yuyv_col = texture2D(yuyv, gl_TexCoord[0]);
 float mask_col = texture2D(mask, gl_TexCoord[1]);

 vec3 even_col = vec3(yuyv_col.r, yuyv_col.g, yuyv_col.a);
 vec3 odd_col = vec3(yuyv_col.b, yuyv_col.g, yuyv_col.a);

 // if shader is in even pos use rga as yuv
 // else shader is in odd position so it use bga as yuv
 vec3 final_yuv_col;
 if (mask_col == 0.0f) final_yuv_col = even_col;
 else final_yuv_col = odd_col;

 vec3 final_rgb_col = convert_yuv_to_rgb(final_yuv_col);

 gl_FragColor = vec4(final_col, 1.0);
}

This code is not tested and it might not work, but it shows idea…

Think I’m getting confused here…
I have 3 textures, and 3 PBOs (1 for each Y, U and V component).
NOTE: If my Y tex is 800x600, my U and V textures are 400x300, so I can’t use one texture for all of them.

I’m binding Y, U, and V to GL_TEXTURE0, GL_TEXTURE1, and GL_TEXTURE2 respectively.

The Y, U, and V data are all in unsigned char format, so I’m using LUMINANCE textures of GL_UNISGNED_BYTE and in my shader just use the .r component (since opengl will take r and use it for g and b as well when it constructs the texture).

I have 3 PBOs (one for each of Y, U, and V), and I thought that when I use it in glTexSubImage2D I would get the following:

PBO_BUFFER_OFFSET(0) - pointer to Y texture data

PBO_BUFFER_OFFSET(1) - pointer to U texture data (note: this will be a lot less data since the U tex is half the width and height of the Y tex).

PBO_BUFFER_OFFSET(2) - pointer to V texture data (note: this will be same size as U)

I’m pretty sure I got something wrong…I just don’t know what it is :wink:

Any help, much appreciated.

cheers,
g.

Originally posted by g0l3m:
[b]
I have 3 PBOs (one for each of Y, U, and V), and I thought that when I use it in glTexSubImage2D I would get the following:

PBO_BUFFER_OFFSET(0) - pointer to Y texture data

PBO_BUFFER_OFFSET(1) - pointer to U texture data (note: this will be a lot less data since the U tex is half the width and height of the Y tex).

PBO_BUFFER_OFFSET(2) - pointer to V texture data (note: this will be same size as U)
[/b]
You misunderstood how the offset works. It seems that you assume that the offset is index into the mBufID_YUV array which is not true. At any time there is only one bound buffer and the offset within the glTexSubImage2D specifies offset from start of that buffer to data which should be used for the texture. For offset 0, the texture will be created from bytes within <0,widthheight-1> range. For offset 1, it will be <1,widthheight> range and so on. If the range does not fit into the buffer, error will be reported.

To upload the three textures using the three PBO you should use something like:

// For Y channel

glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mBufID_YUV[0] ) ;

.. setup Y values to the  buffer.

... bind Y texture...

glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, PBO_BUFFER_OFFSET(0));

// For U channel

glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mBufID_YUV[1] ) ;

.. setup U values to the buffer.

... bind U texture...

glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, PBO_BUFFER_OFFSET(0));

// For V channel

glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mBufID_YUV[2] ) ;

.. setup V values to the buffer.

... bind V texture...

glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, PBO_BUFFER_OFFSET(0));

ahem…it turns out I was being a complete muppet…

To support older hardware, I was using texture rects so the UVs passed to the shader were not normalised…

I moved to PBOs and hardware that supports ARB_texture_non_power_of_two, so my shader moved from sampler2DRect and texture2DRect to sampler2D and texture2D…

But, I was still passing in un-normalised UVs…
Everything works fine now (having corrected the PBO_BUFFER_OFFSET stuff of course).

Thx for the help all.