How to implement VBO double buffering in WebGL? - webgl

I want to increase performance in my WebGL project by setting up VBO double buffering. Although there are plenty of articles on this topic, I failed to find one with a coding example.
I tried the following:
// Initial setup...
var buff1 = gl.createBuffer();
var buff2 = gl.createBuffer();
var buffActive = buff1;
// For each frame, change the verticies data and then ...
gl.bindBuffer(gl.ARRAY_BUFFER, buffActive);
gl.bufferData(gl.ARRAY_BUFFER, vertArray, gl.DYNAMIC_DRAW);
gl.drawArrays(gl.TRIANGLES, start, count);
buffActive = (buffActive === buff2) ? buff1 : buff2;
The calls to gl.bindBuffer and gl.bufferData work fine. However, gl.drawArrays always renders using only the data from buff1. I assumed gl.drawArrays would render using whichever VBO is currently bound to gl.ARRAY_BUFFER, but apparently that's not the case.
Does anyone see what I'm missing?

The issue here is you have bound both buff1 and buff2 to the same buffer, vertArray.
A better thing to do is create two vertex arrays (say, vertArray1 and vertArray2) and
to bind each vertex array to a separate buffer object during the initial setup (and not each frame, because
bufferData() destroys and reinitializes a buffer object's data, which may be an
expensive operation if doing so requires uploading the data to the GPU).
Here's an example:
// Initial setup
var buff1 = gl.createBuffer();
var buff2 = gl.createBuffer();
// Associate the buffer data once during initial setup.
// Note that we use two vertex arrays, vertArray1 and vertArray2,
// rather than one
gl.bindBuffer(gl.ARRAY_BUFFER, buff1);
gl.bufferData(gl.ARRAY_BUFFER, vertArray1, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, buff2);
gl.bufferData(gl.ARRAY_BUFFER, vertArray2, gl.DYNAMIC_DRAW);
var buffActive = buff1;
var vertArrayActive = vertArray1;
// For each frame:
gl.bindBuffer(gl.ARRAY_BUFFER, buffActive);
// No need to re-load the buffer data
gl.drawArrays(gl.TRIANGLES, start, count);
buffActive = (buffActive === buff2) ? Buff1 : buff2;
vertArrayActive = (vertArrayActive === vertArray2) ? VertArray1 : vertArray2;

If you implement VBO double buffering then you need to ensure calls to gl.vertexAttribPointer and gl.enableVertexAttribArray for vertex attribute data (stored as interleaved data in vertArray) are made every time the VBOs switch, rather than just during setup. Otherwise, your vertex attribute pointers will always be referencing data in the first VBO, and that VBO will now only be updated every other frame as a consequence of double buffering. The calls to gl.vertexAttribPointer and gl.enableVertexAttribArray must now be made after switching VBOs and before gl.drawArrays.

Related

how can I update dynamic vertex buffer fastly?

I'm trying to make a simple 3D modeling tool.
there is some work to move a vertex( or vertices ) for transform the model.
I used dynamic vertex buffer because thought it needs much update.
but performance is too low in high polygon model even though I change just one vertex.
is there other methods? or did I wrong way?
here is my D3D11_BUFFER_DESC
Usage = D3D11_USAGE_DYNAMIC;
CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
BindFlags = D3D11_BIND_VERTEX_BUFFER;
ByteWidth = sizeof(ST_Vertex) * _nVertexCount
D3D11_SUBRESOURCE_DATA d3dBufferData;
d3dBufferData.pSysMem = pVerticesInfo;
hr = pd3dDevice->CreateBuffer(&descBuffer, &d3dBufferData, &_pVertexBuffer);
and my update funtion
D3D11_MAPPED_SUBRESOURCE d3dMappedResource;
pImmediateContext->Map(_pVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &d3dMappedResource);
ST_Vertex* pBuffer = (ST_Vertex*)d3dMappedResource.pData;
for (int i = 0; i < vIndice.size(); ++i)
{
pBuffer[vIndice[i]].xfPosition.x = pVerticesInfo[vIndice[i]].xfPosition.x;
pBuffer[vIndice[i]].xfPosition.y = pVerticesInfo[vIndice[i]].xfPosition.y;
pBuffer[vIndice[i]].xfPosition.z = pVerticesInfo[vIndice[i]].xfPosition.z;
}
pImmediateContext->Unmap(_pVertexBuffer, 0);
As mentioned in the previous answer, you are updating your whole buffer every time, which will be slow depending on model size.
The solution is indeed to implement partial updates, there are two possibilities for it, you want to update a single vertex, or you want to update
arbitrary indices (for example, you want to move N vertices in one go, in different locations, like vertex 1,20,23 for example.
The first solution is rather simple, first create your buffer with the following description :
Usage = D3D11_USAGE_DEFAULT;
CPUAccessFlags = 0;
BindFlags = D3D11_BIND_VERTEX_BUFFER;
ByteWidth = sizeof(ST_Vertex) * _nVertexCount
D3D11_SUBRESOURCE_DATA d3dBufferData;
d3dBufferData.pSysMem = pVerticesInfo;
hr = pd3dDevice->CreateBuffer(&descBuffer, &d3dBufferData, &_pVertexBuffer);
This makes sure your vertex buffer is gpu visible only.
Next create a second dynamic buffer which has the size of a single vertex (you do not need any bind flags in that case, as it will be used only for copies)
_pCopyVertexBuffer
Usage = D3D11_USAGE_DYNAMIC; //Staging works as well
CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
BindFlags = 0;
ByteWidth = sizeof(ST_Vertex);
D3D11_SUBRESOURCE_DATA d3dBufferData;
d3dBufferData.pSysMem = NULL;
hr = pd3dDevice->CreateBuffer(&descBuffer, &d3dBufferData, &_pCopyVertexBuffer);
when you move a vertex, copy the changed vertex in the copy buffer :
ST_Vertex changedVertex;
D3D11_MAPPED_SUBRESOURCE d3dMappedResource;
pImmediateContext->Map(_pVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &d3dMappedResource);
ST_Vertex* pBuffer = (ST_Vertex*)d3dMappedResource.pData;
pBuffer->xfPosition.x = changedVertex.xfPosition.x;
pBuffer->.xfPosition.y = changedVertex.xfPosition.y;
pBuffer->.xfPosition.z = changedVertex.xfPosition.z;
pImmediateContext->Unmap(_pVertexBuffer, 0);
Since you use D3D11_MAP_WRITE_DISCARD, make sure to write all attributes there (not only position).
Now once you done, you can use ID3D11DeviceContext::CopySubresourceRegion to only copy the modified vertex in the current location :
I assume that vertexID is the index of the modified vertex :
pd3DeviceContext->CopySubresourceRegion(_pVertexBuffer,
0, //must be 0
vertexID * sizeof(ST_Vertex), //location of the vertex in you gpu vertex buffer
0, //must be 0
0, //must be 0
_pCopyVertexBuffer,
0, //must be 0
NULL //in this case we copy the full content of _pCopyVertexBuffer, so we can set to null
);
Now if you want to update a list of vertices, things get more complicated and you have several options :
-First you apply this single vertex technique in a loop, this will work quite well if your changeset is small.
-If your changeset is very big (close to almost full vertex size, you can probably rewrite the whole buffer instead).
-An intermediate technique is to use compute shader to perform the updates (thats the one I normally use as its the most flexible version).
Posting all c++ binding code would be way too long, but here is the concept :
your vertex buffer must have BindFlags = D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_UNORDERED_ACCESS; //this allows to write wioth compute
you need to create an ID3D11UnorderedAccessView for this buffer (so shader can write to it)
you need the following misc flags : D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS //this allows to write as RWByteAddressBuffer
you then create two dynamic structured buffers (I prefer those over byteaddress, but vertex buffer and structured is not allowed in dx11, so for the write one you need raw instead)
first structured buffer has a stride of ST_Vertex (this is your changeset)
second structured buffer has a stride of 4 (uint, these are the indices)
both structured buffers get an arbitrary element count (normally i use 1024 or 2048), so that will be the maximum amount of vertices you can update in a single pass.
both structured buffers you need an ID3D11ShaderResourceView (shader visible, read only)
Then update process is the following :
write modified vertices and locations in structured buffers (using map discard, if you have to copy less its ok)
attach both structured buffers for read
attach ID3D11UnorderedAccessView for write
set your compute shader
call dispatch
detach ID3D11UnorderedAccessView for write (this is VERY important)
This is a sample compute shader code (I assume you vertex is position only, for simplicity)
cbuffer cbUpdateCount : register(b0)
{
uint updateCount;
};
RWByteAddressBuffer RWVertexPositionBuffer : register(u0);
StructuredBuffer<float3> ModifiedVertexBuffer : register(t0);
StructuredBuffer<uint> ModifiedVertexIndicesBuffer : register(t0);
//this is the stride of your vertex buffer, since here we use float3 it is 12 bytes
#define WRITE_STRIDE 12
[numthreads(64, 1, 1)]
void CS( uint3 tid : SV_DispatchThreadID )
{
//make sure you do not go part element count, as here we runs 64 threads at a time
if (tid.x >= updateCount) { return; }
uint readIndex = tid.x;
uint writeIndex = ModifiedVertexIndicesBuffer[readIndex];
float3 vertex = ModifiedVertexBuffer[readIndex];
//byte address buffers do not understand float, asuint is a binary cast.
RWVertexPositionBuffer.Store3(writeIndex * WRITE_STRIDE, asuint(vertex));
}
For the purposes of this question I'm going to assume you already have a mechanism for selecting a vertex from a list of vertices based upon ray casting or some other picking method and a mechanism for creating a displacement vector detailing how the vertex was moved in model space.
The method you have for updating the buffer is sufficient for anything less than a few hundred vertices, but on large scale models it becomes extremely slow. This is because you're updating everything, rather than the individual vertices you modified.
To fix this, you should only update the vertices you have changed, and to do that you need to create a change set.
In concept, a change set is nothing more than a set of changes made to the data - a list of the vertices that need to be updated. Since we already know which vertices were modified (otherwise we couldn't have manipulated them), we can map in the GPU buffer, go to that vertex specifically, and copy just those vertices into the GPU buffer.
In your vertex modification method, record the index of the vertex that was modified by the user:
//Modify the vertex coordinates based on mouse displacement
pVerticesInfo[SelectedVertexIndex].xfPosition.x += DisplacementVector.x;
pVerticesInfo[SelectedVertexIndex].xfPosition.y += DisplacementVector.y;
pVerticesInfo[SelectedVertexIndex].xfPosition.z += DisplacementVector.z;
//Add the changed vertex to the list of changes.
changedVertices.add(SelectedVertexIndex);
//And update the GPU buffer
UpdateD3DBuffer();
In UpdateD3DBuffer(), do the following:
D3D11_MAPPED_SUBRESOURCE d3dMappedResource;
pImmediateContext->Map(_pVertexBuffer, 0, D3D11_MAP_WRITE, 0, &d3dMappedResource);
ST_Vertex* pBuffer = (ST_Vertex*)d3dMappedResource.pData;
for (int i = 0; i < changedVertices.size(); ++i)
{
pBuffer[changedVertices[i]].xfPosition.x = pVerticesInfo[changedVertices[i]].xfPosition.x;
pBuffer[changedVertices[i]].xfPosition.y = pVerticesInfo[changedVertices[i]].xfPosition.y;
pBuffer[changedVertices[i]].xfPosition.z = pVerticesInfo[changedVertices[i]].xfPosition.z;
}
pImmediateContext->Unmap(_pVertexBuffer, 0);
changedVertices.clear();
This has the effect of only updating the vertices that have changed, rather than all vertices in the model.
This also allows for some more complex manipulations. You can select multiple vertices and move them all as a group, select a whole face and move all the connected vertices, or move entire regions of the model relatively easily, assuming your picking method is capable of handling this.
In addition, if you record the change sets with enough information (the affected vertices and the displacement index), you can fairly easily implement an undo function by simply reversing the displacement vector and reapplying the selected change set.

WebGL: async operations?

I'd like to know if there are any async calls for WebGL that one could take advantage of?
I have looked into Spec v1 and Spec v2 they don't mention anything. In V2, there is a WebGL Query mechanism which I don't think is what I'm looking for.
A search on the web didn't come up with anything definitive. There is this example and is not clear how the sync and async version differ. http://toji.github.io/shader-perf/
Ultimately I'd like to be able to some of all of these asynchronously:
readPixels
texSubImage2D and texImage2D
Shader compilation
program linking
draw???
There is a glFinish operation and the documentation for it says: "does not return until the effects of all previously called GL commands are complete.". To me this means that there are asynchronous operations which can be awaited for by calling Finish()?
And some posts on the web suggest that calling getError() also forces some synchronicity and is not a very desired thing to do after every call.
It depends on your definition of async.
In Chrome (Firefox might also do this now? not sure). Chrome runs all GPU code in a separate process from JavaScript. That means your commands are running asynchronous. Even OpenGL itself is designed to be asynchronous. The functions (WebGL/OpenGL) insert commands into a command buffer. Those are executed by some other thread/process. You tell OpenGL "hey, I have new commands for you to execute!" by calling gl.flush. It executes those commands asynchronously. If you don't call gl.flush it will be called for you periodically when too many commands have been issued. It will also be called when the current JavaScript event exits, assuming you called any rendering command to the canvas (gl.drawXXX, gl.clear).
In this sense everything about WebGL is async. If you don't query something (gl.getXXX, gl.readXXX) then stuff is being handled (drawn) out of sync with your JavaScript. WebGL is giving you access to a GPU after all running separately from your CPU.
Knowing this one way to take advantage of it in Chrome is to compile shaders async by submitting the shaders
for each shader
s = gl.createShader()
gl.shaderSource(...);
gl.compileShader(...);
gl.attachShader(...);
gl.linkProgram(...)
gl.flush()
The GPU process will now be compiling your shaders. So, say, 250ms later you only then start asking if it succeeded and querying locations, then if it took less then 250ms to compile and link the shaders it all happened async.
In WebGL2 there is at least one more clearly async operation, occlusion queries, in which WebGL2 can tell you how many pixels were drawn for a group of draw calls. If non were drawn then your draws were occluded. To get the answer you periodically pole to see if the answer is ready. Typically you check next frame and in fact the WebGL spec requires the answer to not be available until the next frame.
Otherwise, at the moment (August 2018), there is no explicitly async APIs.
Update
HankMoody brought up in comments that texImage2D is sync. Again, it depends on your definition of async. It takes time to add commands and their data. A command like gl.enable(gl.DEPTH_TEST) only has to add 2-8 bytes. A command like gl.texImage2D(..., width = 1024, height = 1024, RGBA, UNSIGNED_BYTE) has to add 4meg!. Once that 4meg is uploaded the rest is async, but the uploading takes time. That's the same for both commands it's just that adding 2-8 bytes takes a lot less time than adding 4meg.
To more be clear, after that 4 meg is uploaded many other things happen asynchronously. The driver is called with the 4 meg. The driver copies that 4meg. The driver schedules that 4meg to be used sometime later as it can't upload the data immediately if the texture is already in use. Either that or it does upload it immediately just to a new area and then swaps what the texture is pointing to at just before a draw call that actually uses that new data. Other drivers just copy the data and store it and wait until the texture is used in a draw call to actually update the texture. This is because texImage2D has crazy semantics where you can upload different size mips in any order so the driver can't know what's actually needed in GPU memory until draw time since it has no idea what order you're going to call texIamge2D. All of this stuff mentioned in this paragraph happens asynchronously.
But that does bring up some more info.
gl.texImage2D and related commands have to do a TON of work. One is they have to honor UNPACK_FLIP_Y_WEBGL and UNPACK_PREMULTIPLY_ALPHA_WEBGL so they man need to make a copy of multiple megs of data to flip it or premultiply it. Second, if you pass them a video, canvas, or image they may have to do heavy conversions or even reparse the image from source especially in light of UNPACK_COLORSPACE_CONVERSION_WEBGL. Whether this happens in some async like way or not is up to the browser. Since you don't have direct access to the image/video/canvas it would be possible for the browser to do all of this async but one way or another all that work has to happen.
To make much of that work ASYNC the ImageBitmap API was added. Like most Web APIs it's under-specified but the idea is you first do a fetch (which is async). You then request to create an ImageBitmap and give it options for color conversion, flipping, pre-multiplied alpha. This also happens async. You then pass the result to gl.texImage2D with the hope being that the browser was able to make do all the heavy parts before it got to this last step.
Example:
// note: mode: 'cors' is because we are loading
// from a different domain
async function main() {
const response = await fetch('https://i.imgur.com/TSiyiJv.jpg', {mode: 'cors'})
if (!response.ok) {
return console.error('response not ok?');
}
const blob = await response.blob();
const bitmap = await createImageBitmap(blob, {
premultiplyAlpha: 'none',
colorSpaceConversion: 'none',
});
const gl = document.querySelector("canvas").getContext("webgl");
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
{
const level = 0;
const internalFormat = gl.RGBA;
const format = gl.RGBA;
const type = gl.UNSIGNED_BYTE;
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
format, type, bitmap);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
}
const vs = `
uniform mat4 u_worldViewProjection;
attribute vec4 position;
attribute vec2 texcoord;
varying vec2 v_texCoord;
void main() {
v_texCoord = texcoord;
gl_Position = u_worldViewProjection * position;
}
`;
const fs = `
precision mediump float;
varying vec2 v_texCoord;
uniform sampler2D u_tex;
void main() {
gl_FragColor = texture2D(u_tex, v_texCoord);
}
`;
const m4 = twgl.m4;
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
const bufferInfo = twgl.primitives.createCubeBufferInfo(gl, 2);
const uniforms = {
u_tex: tex,
};
function render(time) {
time *= 0.001;
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.enable(gl.DEPTH_TEST);
const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 10;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [1, 4, -6];
const target = [0, 0, 0];
const up = [0, 1, 0];
const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);
const world = m4.rotationY(time);
uniforms.u_worldViewProjection = m4.multiply(viewProjection, world);
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, uniforms);
gl.drawElements(gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
Unfortunately this only works in Chrome as of 2018 August. Firefox bug is here. Other browsers I don't know.

Relationship between VertexArray and Attribute [duplicate]

I sometimes find myself struggling between declaring the buffers (with createBuffer/bindBuffer/bufferdata) in different order and rebinding them in other parts of the code, usually in the draw loop.
If I don't rebind the vertex buffer before drawing arrays, the console complains about an attempt to access out of range vertices. My suspect is the the last bound object is passed at the pointer and then to the drawarrays but when I change the order at the beginning of the code, nothing changes. What effectively works is rebinding the buffer in the draw loop. So, I can't really understand the logic behind that. When do you need to rebind? Why do you need to rebind? What is attribute0 referring to?
I don't know if this will help. As some people have said, GL/WebGL has a bunch of internal state. All the functions you call set up the state. When it's all setup you call drawArrays or drawElements and all of that state is used to draw things
This has been explained elsewhere on SO but binding a buffer is just setting 1 of 2 global variables inside WebGL. After that you refer to the buffer by its bind point.
You can think of it like this
gl = function() {
// internal WebGL state
let lastError;
let arrayBuffer = null;
let vertexArray = {
elementArrayBuffer: null,
attributes: [
{ enabled: false, type: gl.FLOAT, size: 3, normalized: false,
stride: 0, offset: 0, buffer: null },
{ enabled: false, type: gl.FLOAT, size: 3, normalized: false,
stride: 0, offset: 0, buffer: null },
{ enabled: false, type: gl.FLOAT, size: 3, normalized: false,
stride: 0, offset: 0, buffer: null },
{ enabled: false, type: gl.FLOAT, size: 3, normalized: false,
stride: 0, offset: 0, buffer: null },
{ enabled: false, type: gl.FLOAT, size: 3, normalized: false,
stride: 0, offset: 0, buffer: null },
...
],
}
// these values are used when a vertex attrib is disabled
let attribValues = [
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
...
];
...
// Implementation of gl.bindBuffer.
// note this function is doing nothing but setting 2 internal variables.
this.bindBuffer = function(bindPoint, buffer) {
switch(bindPoint) {
case gl.ARRAY_BUFFER;
arrayBuffer = buffer;
break;
case gl.ELEMENT_ARRAY_BUFFER;
vertexArray.elementArrayBuffer = buffer;
break;
default:
lastError = gl.INVALID_ENUM;
break;
}
};
...
}();
After that other WebGL functions reference those. For example gl.bufferData might do something like
// implementation of gl.bufferData
// Notice you don't pass in a buffer. You pass in a bindPoint.
// The function gets the buffer one of its internal variable you set by
// previously calling gl.bindBuffer
this.bufferData = function(bindPoint, data, usage) {
// lookup the buffer from the bindPoint
var buffer;
switch (bindPoint) {
case gl.ARRAY_BUFFER;
buffer = arrayBuffer;
break;
case gl.ELEMENT_ARRAY_BUFFER;
buffer = vertexArray.elemenArrayBuffer;
break;
default:
lastError = gl.INVALID_ENUM;
break;
}
// copy data into buffer
buffer.copyData(data); // just making this up
buffer.setUsage(usage); // just making this up
};
Separate from those bindpoints there's number of attributes. The attributes are also global state by default. They define how to pull data out of the buffers to supply to your vertex shader. Calling gl.getAttribLocation(someProgram, "nameOfAttribute") tells you which attribute the vertex shader will look at to get data out of a buffer.
So, there's 4 functions that you use to configure how an attribute will get data from a buffer. gl.enableVertexAttribArray, gl.disableVertexAttribArray, gl.vertexAttribPointer, and gl.vertexAttrib??.
They're effectively implemented something like this
this.enableVertexAttribArray = function(location) {
const attribute = vertexArray.attributes[location];
attribute.enabled = true; // true means get data from attribute.buffer
};
this.disableVertexAttribArray = function(location) {
const attribute = vertexArray.attributes[location];
attribute.enabled = false; // false means get data from attribValues[location]
};
this.vertexAttribPointer = function(location, size, type, normalized, stride, offset) {
const attribute = vertexArray.attributes[location];
attribute.size = size; // num values to pull from buffer per vertex shader iteration
attribute.type = type; // type of values to pull from buffer
attribute.normalized = normalized; // whether or not to normalize
attribute.stride = stride; // number of bytes to advance for each iteration of the vertex shader. 0 = compute from type, size
attribute.offset = offset; // where to start in buffer.
// IMPORTANT!!! Associates whatever buffer is currently *bound* to
// "arrayBuffer" to this attribute
attribute.buffer = arrayBuffer;
};
this.vertexAttrib4f = function(location, x, y, z, w) {
const attrivValue = attribValues[location];
attribValue[0] = x;
attribValue[1] = y;
attribValue[2] = z;
attribValue[3] = w;
};
Now, when you call gl.drawArrays or gl.drawElements the system knows how you want to pull data out of the buffers you made to supply your vertex shader. See here for how that works.
Since the attributes are global state that means every time you call drawElements or drawArrays how ever you have the attributes setup is how they'll be used. If you set up attributes #1 and #2 to buffers that each have 3 vertices but you ask to draw 6 vertices with gl.drawArrays you'll get an error. Similarly if you make an index buffer which you bind to the gl.ELEMENT_ARRAY_BUFFER bindpoint and that buffer has an indice that is > 2 you'll get that index out of range error. If your buffers only have 3 vertices then the only valid indices are 0, 1, and 2.
Normally, every time you draw something different you rebind all the attributes needed to draw that thing. Drawing a cube that has positions and normals? Bind the buffer with position data, setup the attribute being used for positions, bind the buffer with normal data, setup the attribute being used for normals, now draw. Next you draw a sphere with positions, vertex colors and texture coordinates. Bind the buffer that contains position data, setup the attribute being used for positions. Bind the buffer that contains vertex color data, setup the attribute being used for vertex colors. Bind the buffer that contains texture coordinates, setup the attribute being used for texture coordinates.
The only time you don't rebind buffers is if you're drawing the same thing more than once. For example drawing 10 cubes. You'd rebind the buffers, then set the uniforms for one cube, draw it, set the uniforms for the next cube, draw it, repeat.
I should also add that there's an extension [OES_vertex_array_object] which is also a feature of WebGL 2.0. A Vertex Array Object is the global state above called vertexArray which includes the elementArrayBuffer and all the attributes.
Calling gl.createVertexArray makes new one of those. Calling gl.bindVertexArray sets the global attributes to point to the one in the bound vertexArray.
Calling gl.bindVertexArray would then be
this.bindVertexArray = function(vao) {
vertexArray = vao ? vao : defaultVertexArray;
}
This has the advantage of letting you set up all attributes and buffers at init time and then at draw time just 1 WebGL call will set all buffers and attributes.
Here is a webgl state diagram that might help visualize this better.

OpenGL ES 2.0 - memory management in drawing lines (graphing)

I finally got some functioning code to draw lines (in Xamarin/monotouch)
//init calls
Context = new EAGLContext (EAGLRenderingAPI.OpenGLES2);
DrawableDepthFormat = GLKViewDrawableDepthFormat.Format24;
EAGLContext.SetCurrentContext (Context);
effect = new GLKBaseEffect ();
effect.UseConstantColor = true;
effect.ConstantColor = new Vector4 (1f, 1f, 1f, 1f); //white
GL.ClearColor (0f, 0f, 0f, 1f);//black
public void DrawLine(float[] pts) {
//generate, bind, init
GL.GenBuffers (1, out vertexBuffer);
GL.BindBuffer (BufferTarget.ArrayBuffer, vertexBuffer);
GL.BufferData (BufferTarget.ArrayBuffer, (IntPtr) (pts.Length * sizeof (float)), pts, BufferUsage.DynamicDraw);
// RENDER //
effect.PrepareToDraw ();
//describe what's going to happen
GL.EnableVertexAttribArray ((int) GLKVertexAttrib.Position);
GL.VertexAttribPointer ((int) GLKVertexAttrib.Position, 2, VertexAttribPointerType.Float, false, sizeof(float) * 2, 0);
GL.DrawArrays (BeginMode.LineStrip, 0, pts.Length/2);
}
I have a couple questions.
Is this approach for drawing lines optimal? Are there any suggested improvements (i.e. antialiasing, etc..)
GL.Clear (ClearBufferMask.ColorBufferBit);
effect.ConstantColor = new Vector4 (1f, 1f, 1f, 1f);
DrawLine (line);
effect.ConstantColor = new Vector4 (1f, 0f, 1f, 1f);
DrawLine (line2);
Does all the memory associated with the line disappear when I call GL.Clear()? i.e. do I have to do any memory cleanup, or can I just keep calling GL.Clear() followed by DrawLine() and not worry about memory management?
I'm planning on using these functions for graphing. If the underlying data changes (but I have the same number of lines, is there a subset of functions that I can call to more efficiently update the lines?
GL.GenBuffers (1, out vertexBuffer) creates a buffer on the GPU and has to be deleted after the usage. In most cases you create buffer to push data to GPU which will not be updated frequently and are used to draw those data many times. There is probably a flag to stream the data (instead of DynamicDraw) for constant updating though. You could use that to reuse the same buffer but it would probably be best to just push the data pointer directly from the CPU: Lose all 3 lines concerning the buffer and insert pts into VertexAttribPointer instead of 0 for the last argument.
You say you will be using this for graph drawing. If the graph data will not be modified every frame and you can compute all the points you still might want to benefit from buffers. Instead of trying to push every line to its own buffer try pushing all the lines to a single buffer (even axis can be there). Use GL.DrawArrays (BeginMode.LineStrip, 0, pts.Length/2) to draw specific lines as last 2 arguments control the range in current buffer to draw (to draw 5th line only you would write GL.DrawArrays(BeginMode.LineStrip, 5*2, 2)). So when the graph data should update; delete the current buffer, create a new buffer, push the data to buffer, bind the buffer, set the vertex pointer and then just keep calling the draw method.
GLClear has nothing to do with memory cleanup at all. It will only clear (set values) the buffers attached to your frame buffer, in your case it will set all the pixels in your render buffer to the color you set in ClearColor. Nothing more. Other common cases are to also clear depth buffer, stencil buffer...
As for all the optimization and anti-aliasing it all depends on what you are doing, there is no general answer. Though if your scene gets too edgy try to search around for multisampling.

drawElements use

I create a little application in WebGL, I have two objects which move, a cube and a sphere.
I modify objects with shaders, each object have it shader. So I want update objects on the display at determine time, for this I use drawElements function.
For each object I have a buffer which contains indices of faces in vertices buffer :
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STREAM_DRAW);
indexCount = indices.length;
indices is an array which contains values of indices of each faces. (3 values per face, we work with triangles).
So, after this, for draw triangles of the object I do :
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_SHORT, 0);
But I have nothing on the screen, and I have this warning :
WebGL: INVALID_OPERATION: drawElements: attribs not setup correctly
What's could be my error ?
Thanks
I think you missed codes which link javascript vertex buffer to shader's attributes before drawElements().
e.g:
gl.bindBuffer(gl.ARRAY_BUFFER, meshVertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,
meshVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, meshIndexBuffer);
gl.drawElements(gl.TRIANGLES, meshIndexBuffer.numberOfItems, gl.UNSIGNED_SHORT, 0);

Resources