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


#1

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.


#2

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


#3

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.


#4

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 @Hypersomniac can be made aware of it.

Thanks


#5

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.


#6

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 @Hypersomniac can read this feedback. Thanks.


#7

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.