How to modify an instance of gpu.types.GPUVertBuf?

Hi,
in the GPU drawing examples (/doc/python_api/examples/), they all create static batches. You create it and then render it forever.
But in some cases you want to draw something that changes with time.

The only sample I could find that draws dynamic geometry does it by recreating a new GPUVertBuf on every frame (https://developer.blender.org/F4765373).

How can you modify the contents of a preexisting GPUVertBuf object? Do you have to call attr_fill again, with different data? I guess what I’m looking for is functions that represent glBufferData or glMapBuffer.

I’m not very familiar with this API, but have you tried just calling attr_fill with different data?

Hi @brecht, if you do that you get a “Can’t fill, static buffer already in use” error message, from this line: https://developer.blender.org/diffusion/B/browse/master/source/blender/python/gpu/gpu_py_vertex_buffer.c;87786566929610e8717b859cc6e61637d70a9ab0$203

So while looking for other ways I noticed that the BGL module is still around 2.80 (I thought it was going to be removed), so we can manually create VBOs for dynamic use, using OpenGL examples around the web as reference. It takes more code but then you have absolute control.

UPDATE: this is now available on BGL, check the last posts in this thread for guidance.


ORIGINAL POST:
To others in the future: it’s not possible to use raw OpenGL with VBOs for drawing, because to do that you need to be able to send a “null pointer” value to a function (glVertexAttribPointer), but the BGL wrapper over that function expects a bgl.Buffer object – even if you fill this bgl.Buffer with “0” it’ll not be a null value (it’ll be the pointer to the bgl.Buffer object, which is definitely not null).

This problem has been talked about in the past, there are a few mailing-list emails and forum posts about it, but the problem persists. I hope @fclem can be made aware of it.

Thanks

2 Likes

there is an example here that does take input to create something dynamic using uniforms

https://docs.blender.org/api/blender2.8/gpu.html#generate-a-texture-using-offscreen-rendering

look inside draw() you can see the uniforms feed data to the shader. This is also the standard way the new API of OpenGL is doing this. If you don’t like the computations then you will need diffident shaders and change the shader each time you want to do something else by binding the glProgram to the appropriate shader.

Please note I have zero experience with GPU module, I used to use BGL but once Blender devs announced that they wanted to move away from BGL and because I had already loads of issues with debugging I decided to customize the Blender code itself. So I have not touched bgl for month now.

If I remember correctly bgl.buffer is nothing special just a pointer to a c array so I don’t know if numpy can help here with its arrays.

Generally speaking shaders should be precompiled and not generated runtime because Python could severely slow you down.

Hi @kilon,
Thank you for the example.

If you look in the source of draw_circle_2d, it’s creating a new vertex buffer and shader object on each draw call, which is very wasteful.

You also can’t draw other primitive types without creating a whole new gpu.types.Batch object, when in raw OpenGL you can redraw the same data multiple times with different primitive types because it’s a simple parameter in the drawing function.

Lastly, in PyOpenGL they use c_types.voidptr or something like that to indicate a null pointer, but the BGL wrapper doesnt accept anything other than of bgl.Buffer type, as it’s defined that way in the source.

I hope that @fclem can read this feedback. Thanks.

It really depends what you want to, the example I linked it was just to show the use of uniforms

if you do not want to generate new vertex buffers and optimise the draw process then you will have to make your own circle_2d assuming you want to draw circles. If you want to move things around then you can have a face that is moved via translation on the uniforms.

Of course if you aim for a large collection of primitives , you should not be using this approach anyway because as I said python will become the bottleneck here.

What I would have done , have one face, one vertex buffer, one index buffer and one texture and then use Cairo to draw the actual primitives and then just use the texture that Cairo would create (basically a simple image) with the GPU module on top of the face.

https://www.cairographics.org/
https://www.cairographics.org/OpenGL/
http://www.tortall.net/mu/wiki/CairoTutorial

Cairo is coded in C so you will be getting far higher performance per draw call. Cairo is by far the most popular vector graphics library out there with many popular users, one of them being Firefox.

Of course if you want to do 3d primitives, then Cairo cannot help you there.

Concering GPUVertexBuf , you are correct there is no way to edit them directly. Which is why circle_2d generates new ones each time.

Any news on glVertexAttribPointer ?

I applied this patch here and now glVertexAttribPointer works:

https://developer.blender.org/D2404

And since I yesterday it works in master as well - thank you!

1 Like

Seems to be this commit:
https://developer.blender.org/rB85915ae1aad761fc79eccf005e68a2334ad6b81c

That’s awesome, high speed VBO drawing in viewport awaits! We should work on a simple template for people to grab.

2 Likes

I just made some more tests and glVertexAttribPointer only works when passing none. When using Buffer() it does not.

This code does not work:

col_attr_buf = Buffer(GL_INT, 1)
col_attr_buf[0] = 12
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 24, col_attr_buf)

After applying this patch (https://developer.blender.org/D2404), the following code does work as expected:

glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 24, 12)

@brecht how can we translate the following line to BGL?
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3* sizeof(float))

1 Like

I saw on the wiki that @fclem is responsible for the OpenGL module,so I’m tagging him as well.

Fix committed so you can pass integers as well:
https://developer.blender.org/rB5cfeba7

2 Likes

Confirmed, thank you very much!

Hi @GottfriedHofmann and@ RNavega could you point me to a good example of updating vertex buffer geom as you are describing. I’m just starting to delve into updating some rather complex (to me) bgl drawing modules. My first dive would be to just draw several points that the user can click and drag in 3D.

@patmo141 a great little sample that I keep coming back to is this one: https://cognitivewaves.wordpress.com/opengl-vbo-shader-vao/#shader-with-vertex-buffer-object

What this thread was about was the bgl.glVertexAttribPointer() function, the last parameter of it is an offset pointer to the first element that you want to begin drawing, from the buffer currently bound.
With the latest BGL, on that last parameter, you can specify the start of the buffer (use None), or some other index into the buffer (use an integer).

We usually draw the whole buffer – all its vertices – so 99% of the time you’ll use None as that parameter to use all data.
When you do want to use an index into the buffer so as to skip part of it, consult this: https://stackoverflow.com/a/39684775