I’m looking for some advice/point me in the right direction. Basically I’m trying to go from building a shape using a VAO/VBO where you directly specify all the information, and migrate the code to a place where I can specify the positions/numbers of shapes dynamically.
So here’s some of my current thoughts at the moment:
Seems like each shape instance needs to have its own buffers and reference to the VBO id
Not sure about the VAO do I need multiple VAO’s?
I’d like the shape to initialize it’s own buffers and fill them at the appropriate times.
Looking for some direction on the next step, any help is much appreciated
You should look at elect’s examples including this one and post your question on the official JogAmp forum as your question seems to be specific to our API.
Instead of reinventing the wheel, I advise you to study the source code of the most famous engines/frameworks with JOGL renderers, for example JMonkeyEngine 3, LibGDX, JogAmp’s Ardor3D Continuation, Java3D, …
A basic/starting approach corresponds to your current thoughts, each shape/mesh with its own buffer id. Once you loaded the geometry on the gpu, if you don’t need it anymore on the cpu, you can clear it (BufferUtils.destroyDirect for direct buffers, because otherwise you don’t have the security they are gonna be removed). The mesh will obviously have also the vbo id as you say.
Vao was a brilliant idea poorly implemented.
It is useful just to avoid to set up again and again the vertex layout, that is glVertexAttribBinding/glVertexAttribFormat/glEnableVertexAttribArray and so on. But actually if you manage your vertex attributes wisely (that Semantic.java gives you very good chances) you just need to create only one (or very few) vao and keep it bound forever.
Think about it, most of meshes (usually) are gonna need always the same vertex attributes: position, normal, texture coordinates, matrices and so on.
You do not need to change the vertex attribute format, you need to change only the binding buffer where the shader will fetch the data from at the given time
Remember also to clean resources when you are done with one mesh on the gpu (buffer deletion and so on)
Thank you! this is exactly the information I was looking for to help me connect Step A to Step B. It’s also pretty awesome to see this response since your tutorial is actually the one I was working off of
I was studying NASA’s WorldWind’s source code and what I see them doing is using this Geometry class to group the buffers involved. It looks like WorldWind caches the Geometry and then recalls it if needed instead of purging it. My version isn’t caching it or recalling it since I’m working on such a simple level(I am just trying to understand the basics of the rasterization process). Are you saying that I should purge the geometry after it has been loaded by disposing the buffers in this class? Or is this a dumb approach entirely?
public class Geometry {
private final int[] mode;
private final int[] count;
private final int[] size;
private final int[] glType;
private final Buffer[] buffer;
public Geometry() {
this.mode = new int[4];
this.count = new int[4];
this.size = new int[4];
this.glType = new int[4];
this.buffer = new Buffer[4];
}
public int getMode(GLBuffer type) {
return this.mode[type.id()];
}
public void setMode(GLBuffer type, int mode) {
this.mode[type.id()] = mode;
}
public int getCount(GLBuffer type) {
return this.count[type.id()];
}
public int getSize(GLBuffer type) {
return this.size[type.id()];
}
public int getGLType(GLBuffer type) {
return this.glType[type.id()];
}
public Buffer getBuffer(GLBuffer type) {
return this.buffer[type.id()];
}
public void setElementData(int mode, int count, int[] src) {
this.setMode(ELEMENT, mode);
this.buffer[ELEMENT.id()] = GLBuffers.newDirectIntBuffer(src);
this.size[ELEMENT.id()] = 1;
this.glType[ELEMENT.id()] = GL.GL_UNSIGNED_INT;
this.count[ELEMENT.id()] = count;
}
public void setVertexData(int size, float[] src) {
this.buffer[VERTEX.id()] = GLBuffers.newDirectFloatBuffer(src);
this.size[VERTEX.id()] = size;
this.glType[VERTEX.id()] = GL.GL_FLOAT;
this.count[VERTEX.id()] = 3;
}
public long getSizeInBytes(GLBuffer type) {
return this.bufferSize(type);
}
private long bufferSize(GLBuffer type) {
long sz = 0L;
if (this.buffer[type.id()] != null) {
sz = this.sizeOf(this.glType[type.id()]) * this.getCount(type) * this.size[type.id()];
}
return sz;
}
private long sizeOf(int glType) {
long sz = 0L;
switch (glType) {
case GL2.GL_BYTE:
sz = 1L;
break;
case GL2.GL_SHORT:
case GL2.GL_UNSIGNED_SHORT:
sz = 2L;
break;
case GL2.GL_INT:
case GL2.GL_UNSIGNED_INT:
case GL2.GL_FLOAT:
sz = 4L;
break;
case GL2.GL_DOUBLE:
sz = 8L;
break;
}
return sz;
}
I’m not really trying to build an optimal professional engine for the purpose of making a game, I’m trying to understand the pipeline. But, I guess thanks for letting me know.
I’m not sure how often it will need to be regenerated. I think that the shape will be static. Ie, a triangle will start the same as any triangle and then be skewed/scaled based on the that particular instance. So I guess the basic geometry would be static before being rasterized.
Good, then I’d say to start with a simple and basic approach and create a Mesh class. I mean, if you want to do something similar you can do it of course, but I always prefer starting with a simple approarch first and then optimize later, if I feel the needing.
For example, keep everything inside your Mesh class and then move all the buffer stuff to a Geometry class when you see Mesh growing too much.
Write a generic mesh handiling vbo/ibo and then you have multiple choise, you may want to hardcode the geometry initialization in the constructor, or passing it from the extern, or extending the Mesh for each of the shape you want to build, if they are standards (such as cube/sphere/etc), and do the init there…
Once you upload the geometry to your vbo/ibo, delete the direct buffer
I tried to add the z coordinate to the Triangle (my code evolved directly from your hello triangle) ha! it totally exploded in my face with GL errors. Tracked the issue down and fixed it:
(I changed all the vec2s to vec3s but this piece slipped by me)
Shader status invalid: 0(37) : error C1068: too much data in type constructor
I tried to create a second Triangle with different properties but there seems to be a problem. I think I need to do something with the VBO/IBO/Transform pointer offsets.
Will get back to this later today.
GLDebugEvent[ id 0x502
type Error
severity High: dangerous undefined behavior
source GL API
msg GL_INVALID_OPERATION error generated. has not been linked, or is not a program object.
when 1461089778622
source 4.5 (Core profile, arb, debug, compat[ES2, ES3, ES31, ES32], FBO, hardware) - 4.5.0 NVIDIA 364.72 - hash 0x7fc2948c]
I found it. I have the code doing what I want it to do. I’m finally in place where I feel I have control over the pipeline and I can see the results of the things I am doing to measure them.
I’ve been thinking of glsl as a procedure that takes instructions and places them in the local memory of the gpu and then streams/batches data in to be operated on. Since the instructions are so close to the gpu and the gpu is optimized for floating point calculations it’s very efficient. When the batch is complete the instructions are discarded in favor of the next instructions for the next data set. I could be wrong with my visualizing but that’s the mental model that I’ve built from my studying of the architecture thus far. It seems very similar to part of some parallel processing algorithms on the standard cpu where instructions are loaded in the L1 cache and then data is brought to the cpu in batches to be worked on. This avoids an expensive heap traversal during an operation since all the information is in the L1 cache already.
Ps: if you by measuring meant profiling, remember that for measuring gpu time, you have to use gpu timers
Actually glsl code gets first translated to byte code and then to gpu code, you can have a look at this post.
Anyway, if you think about glsl in “graphics terms”, today is a really limited vision… nowadays you have much more freedom given that since version 4.3 we have now Compute Shader. Under these conditions modern OpenGL has became a very powerful api integrating both gpgpu and graphics together in the same api.
You may want to take a look to the computing samples here to have an idea.