Updating VBO of indices via transform feeback

I’m trying (and failing) to update a VBO of indices via transform feedback. This index VBO is used to connect points within another VBO of positions.

I’m having a hard time figuring out the correct way to bind and update the index VBO. Here’s what I have so far (just the update and draw functions).

void IndexTransformFeedbackApp::update()
{
    gl::ScopedState		stateScope( GL_RASTERIZER_DISCARD, true );


    // UPDATE POSITIONS --------------------------------------------------------
    // This works great so far
    {
	gl::ScopedGlslProg	glslScope( mPointUpdateGlsl );
	gl::ScopedVao		vaoScope( mVao[mSourceIndex] );
	
	gl::bindBufferBase( GL_TRANSFORM_FEEDBACK_BUFFER, 0, mPositions[mDestinationIndex] );
	gl::bindBufferBase( GL_TRANSFORM_FEEDBACK_BUFFER, 1, mVelocities[mDestinationIndex] );
	
	// We begin Transform Feedback, using the same primitive that
	// we're "drawing". Using points for the particle system.
	gl::beginTransformFeedback( GL_POINTS );
	gl::drawArrays( GL_POINTS, 0, POINT_NUM );
	gl::endTransformFeedback();
	
        mPositionTexture = gl::BufferTexture::create( mPositions[mDestinationIndex], GL_RGBA32F );
    }

    // UPDATE INDICES ----------------------------------------------------------
    // This is where I'm running into issues
    if( mLineUpdateGlsl ){
	
	gl::ScopedGlslProg	glslScope( mLineUpdateGlsl );
	gl::ScopedVao		vaoScope( mLineVao[mSourceIndex] );
            
        // TODO: bind to mPositionTexture

        gl::bindBufferBase( GL_TRANSFORM_FEEDBACK_BUFFER, 0, mLineIndices[mDestinationIndex] );
	
        gl::beginTransformFeedback( GL_POINTS );
        gl::drawArrays( GL_POINTS, 0, POINT_NUM * POINT_NUM );
        gl::endTransformFeedback();
    }

    std::swap( mSourceIndex, mDestinationIndex );
}

void IndexTransformFeedbackApp::draw()
{
    gl::ScopedMatrices scpMatrx;
    gl::setMatrices( mCam );

    gl::clear( Color( 0, 0, 0 ) );
    {
        gl::ScopedBlendAdditive scpAdd;
        gl::enableAdditiveBlending();
        gl::setDefaultShaderVars();
	
        // draw points
        mPointBatch[mSourceIndex]->drawInstanced( POINT_NUM );

        // draw lines
        mLineBatch[mSourceIndex]->draw();
    }
}

When you run this, the points (spheres) update and draw correctly. The lines just blink, which tells me that it’s swapping between a buffer that has the indices defined and the nulled buffer.

Any thoughts or any more info needed to help figure this out?

A more complete code snippet can be found here:

Thanks.

Hi Greg,

would it be possible to add the shader files to the Gist? Then I could try to run it and perhaps help you find the issue.

-Paul

My knight in shining armor…
I threw everything up in a new repo

Thanks Mr. Houx.

You wrote gl::vertexAttribPointer when binding the index buffer to the vao. Try gl::vertexAttribIPointer and make sure the in variable type in the glsl is uint. The *I variant is for integers.

Thanks Ryan. Nice catch. Unfortunately, that didn’t fix it.

Even after changing both of the shader vars from int to uint?

Nope, no dice.

Do you know of any working samples out there that does something similar?

Yo,

I’ve ran your sample (with a few minor fixes to make it work on Windows), but so far haven’t been able to fix it. I’ve set the frame rate to 1, so it is easier to see the lines blink. When mSourceIndex == 1, the lines disappear. Even if the lineUpdate.vert is just passing the input to the output.

I’m not even sure you can write to an index buffer using Transform Feedback. Perhaps @ryanbartley has more info on that, as he is our TFB king. Sorry for coming up empty-handed.

-Paul

Thanks for trying, Paul. Yeah, I started questioning if you can write to an index buffer via transform feedback too, but thought maybe there was some other error in play.

Will continue researching…

I can take a deeper dive later tonight but in the meantime, I’d pull the data down and echo it to the console just to see if it’s trash or the problem lies somewhere else. As far as I know you can use an index buffer for TF, but just in case that’s not the case - as i’ve never done it - when you’re binding the vbo to the vao use the gl::ScopedBuffer constructor that allows you to pass in what the buffer is and pass in GL_ARRAY_BUFFER. There should be no difference. Also, I’d say, throw a CI_CHECK_GL() in there to see if you’re doing something specifically wrong.

1 Like

Good points. I don’t think there’s trash written to the buffer, though. Nothing is written at all. If you fill the buffer with proper data on creation, the lines don’t blink, meaning that it can read the buffers as intended.

Interesting, CI_CHECK_GL() throws a GL_INVALID_OPERATION error after gl::drawArrays().

...
gl::beginTransformFeedback( GL_POINTS );
gl::drawArrays( GL_POINTS, 0, POINT_NUM * POINT_NUM );
CI_CHECK_GL(); // <- GL_INVALID_OPERATION
...

At least now I have some new info to play with. Thanks.

1 Like

This problem really interested me and I gave it a try tonight. So there are a few bugs in the code that contributed to this problem.

75:  .attribLocation( "vIndex",	0 );
220: ci::gl::vertexAttribIPointer( LINE_INDEX, 1, GL_UNSIGNED_INT, 0, 0 );
221: ci::gl::enableVertexAttribArray( LINE_INDEX );

These attribute indices are not consistent. LINE_INDEX is 3 for some reason, and the glsl is expecting this attribute at location 0. For the sake of simplicity I changed LINE_INDEX to 0.

220: ci::gl::vertexAttribIPointer( LINE_INDEX, 1, GL_UNSIGNED_INT, 0, 0 );

The stride (second to last parameter) is 0, which possibly introduces undefined behavior (I didn’t check the GL spec for this; correct me if I’m wrong). It didn’t break on my AMD driver, but I changed it to sizeof( uint32_t ).

I found out that XFB doesn’t seem to work with GL_ELEMENT_ARRAY_BUFFER. But I figured I can get away with changing the target of the indices VBO before issuing XFB and drawElements(). I’d still keep the target as GL_ELEMENT_ARRAY_BUFFER when I construct the gl::Vbo. And before I bind the source buffer for XFB, I do

for( int i = 0; i < 2; ++i )
    mLineIndices[i]->setTarget( GL_ARRAY_BUFFER );

and before rendering lines I do

for( int i = 0; i < 2; ++i )
    mLineIndices[i]->setTarget( GL_ELEMENT_ARRAY_BUFFER );

Now the trick is that we can no longer use VAO for XFB because it sort of freezes the binding target of our indices VBO. I’d get rid of mLineVao and bind the source buffer in old fashioned way:

gl::ScopedBuffer sccopeBuffer( mLineIndices[mSourceIndex] );
ci::gl::vertexAttribIPointer( 0, 1, GL_UNSIGNED_INT, sizeof( uint32_t ), 0 );
ci::gl::enableVertexAttribArray( 0 );

Last but not least, this line has a bug:

274: gl::drawArrays( GL_POINTS, 0, POINT_NUM * POINT_NUM );

It should be POINT_NUM * POINT_NUM * 2 for obvious reason.

Now if you change the XFB glsl to a pass-thru it’ll work.

1 Like

Good job Phil. I’d also do GL_INTERLEAVED_ATTRIBS instead of SEPARATED because SEPARATED normally means more than one vbo or buffer object. It obviously works without it as Phil has found but that’s because it’s leaning on the driver to correct it. Also, as far as stride, the original 0 is correct as per the spec the stride…

Specifies the byte offset between consecutive generic vertex attributes. If stride is 0, the generic vertex attributes are understood to be tightly packed in the array. The initial value is 0.

Being that there’s only one attribute and it’s tightly packed, 0 is the way to go. I’m also not sure you have to get rid of the vao. You’d just probably not be allowed to bind the buffer as an ELEMENT_ARRAY_BUFFER when enabling the vao, because the index buffer isn’t consumed by the shader. Most likely the big problems were the attribLocation problem and how you bind it to the vao.

Thanks for pointing that out, Ryan. I felt the same way about VAO, I didn’t have time to try it out. Also it may seem awkward and hacky to change the target all the time, but it’s because of the way cinder api works, it’ll look much nicer in native GL.

There’s also a constructor for gl::ScopedBuffer where you can pass the binding point for the buffer without having to change it internally like this…

auto vbo = gl::Vbo::create( GL_ELEMENT_ARRAY_BUFFER );
...
gl::ScopedBuffer scopeBuffer( vbo, GL_ARRAY_BUFFER );
```

Thanks Phil and Ryan, that was really helpful. I have things working now. The big issue was that I was getting a GL_INVALID_OPERATION error with the target of GL_ELEMENT_ARRAY_BUFFER. So switching that on render, resolved that.

Ryan, as far as the ScopedBuffer constructor goes, I don’t see a way to pass the binding point like you had mentioned, unless I misunderstand what you mean. The header only has the following…

ScopedBuffer( const BufferObjRef &bufferObj );
ScopedBuffer( GLenum target, GLuint id );

Thanks again guys. Huge help.

Sorry, I hadn’t looked it up before writing that. You’d use the second one like so…
gl::ScopedBuffer scopeBuffer( GL_ARRAY_BUFFER, mLineVbo->getId() );

You’re welcome. I’ve added the following method in a previous PR, which will be included in the 0.9.1 release:

void BufferObj::bind( GLenum target ) const;

A bit late and maybe a bit off topic. But while googliing around for a transform feedback based particle system with ribbons I only found some posts on this forum. I have been working on something similar as described here, where I use an index buffer to select positions that are used to make ribbons. I’ve published my (wip) experiments here https://bitbucket.org/roxlu/2018-002-gpu-ribbons.