1

I have narrowed down the issue to this. I have two attributes pointed at the exact same data. This works fine when build in native C++. However, when built with emscripten, the javascript console shows the following error on each frame:

'glDrawArrays: attempt to access out of range vertices in attribute 1'

When I comment out the 'glEnableVertexAttribArray' line to enable the second attribute, I don't get this error.

Below is my code. I'll start with the data buffer creation:

GLfloat rectangleData[] =
{
    -.5f, -.5f,     0,1,
    -.5f, .5f,      0,0,
    .5f, .5f,       1,0,
    .5f, -.5f,      1,1,
    -.5f, -.5f,     0,1
};
glGenBuffers(1, &rectangleBuffer);
glBindBuffer(GL_ARRAY_BUFFER, rectangleBuffer);
glBufferData(
        GL_ARRAY_BUFFER, sizeof(rectangleData),
        rectangleData, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);

Here is a relevant excerpt from my textured quad drawing code:

glBindBuffer(GL_ARRAY_BUFFER, rectangleBuffer);

int vertexPosition = Shader::getParameterInfo("vertexPosition")->id;
glVertexAttribPointer(
        vertexPosition, 2, GL_FLOAT,
        GL_FALSE, 16, BUFFER_OFFSET(0));
glEnableVertexAttribArray(vertexPosition);

int vertexTexCoord = Shader::getParameterInfo("vertexTexCoord")->id;
glVertexAttribPointer(
        vertexTexCoord, 2, GL_FLOAT,
        GL_FALSE, 16, BUFFER_OFFSET(0));
glEnableVertexAttribArray(vertexTexCoord);

glDrawArrays(GL_TRIANGLE_FAN, 0, 5);

Notice that I've adjusted the second attribute to point to the same data as the first (to reduce complexity while debugging). I'm pretty stumped here and could really use a fresh/experienced perspective.

EDIT: Here's what BUFFER_OFFSET looks like:

#define BUFFER_OFFSET(i) ((char *)NULL + (i))

Source: How to cast int to const GLvoid*?

EDIT: For what it's worth, here is the equivalent Emscripten generated JS code. I'll post any JS code this references if requested.

dest=$rectangleData; src=2328; stop=dest+80|0; do {
HEAP32[dest>>2]=HEAP32[src>>2]|0; dest=dest+4|0; src=src+4|0; } while
((dest|0) < (stop|0));
 _glGenBuffers(1,(2300|0));
 $30 = HEAP32[2300>>2]|0;
 _glBindBuffer(34962,($30|0));
 _glBufferData(34962,80,($rectangleData|0),35044);
 _glBindBuffer(34962,0);

$11 = HEAP32[2300>>2]|0;
 _glBindBuffer(34962,($11|0));
 $12 = (__ZN8platform6Shader16getParameterInfoEPKc(17356)|0);
 $13 = HEAP32[$12>>2]|0;
 $vertexPosition = $13;
 $14 = $vertexPosition;
 _glVertexAttribPointer(($14|0),2,5126,0,16,(0|0));
 $15 = $vertexPosition;
 _glEnableVertexAttribArray(($15|0));
 $16 = (__ZN8platform6Shader16getParameterInfoEPKc(17379)|0);
 $17 = HEAP32[$16>>2]|0;
 $vertexTexCoord = $17;
 $18 = $vertexTexCoord;
 _glVertexAttribPointer(($18|0),2,5126,0,16,(0|0));
 $19 = $vertexTexCoord;
 _glEnableVertexAttribArray(($19|0));
 _glDrawArrays(6,0,5);

Edit: Better yet, I'll provide a link to the JS code running on github, and the C++ code too (it's near the bottom in "drawImage()"):

https://rawgit.com/jon-heard/Native-WebGL-framework/c134e35ac94fdf3243a9662353ad2227f8c84b43/Native-WebGL-framework/web/index.html

https://github.com/jon-heard/Native-WebGL-framework/blob/c134e35ac94fdf3243a9662353ad2227f8c84b43/Native-WebGL-framework/src/platform/draw.cpp

Community
  • 1
  • 1
Jonathan Heard
  • 606
  • 1
  • 5
  • 14
  • Couple of questions. Is `sizeof(rectangleBuffer)` indeed `20 * sizeof(GLfloat)` and not size of a pointer? And what does `BUFFER_OFFSET` do? – Kirill Dmitrenko Nov 25 '16 at 11:21
  • @KirillDmitrenko: If OP wrote it exactly that way, then yes, `sizeof(rectangleData)` will evaluate to the total size of the array (it would not work, if the array were passed as function parameter or pointer degradation. – datenwolf Nov 25 '16 at 12:03
  • How does the corresponding javascript code look like? The C++ looks correct. – BDL Nov 25 '16 at 14:50

4 Answers4

3

The issue is you have a single vertex shader that ALWAYS USES 2 ATTRIBUTES

var gl = document.createElement("canvas").getContext("webgl");
var program = twgl.createProgramFromScripts(gl, ["vs", "fs"]);

log("list of used attributes");
log("-----------------------");

var numAttribs = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
for (var ii = 0; ii < numAttribs; ++ii) {
  var attribInfo = gl.getActiveAttrib(program, ii);
  if (!attribInfo) {
    break;
  }
  log(gl.getAttribLocation(program, attribInfo.name), attribInfo.name);
}

function log(...args) {
   var div = document.createElement("div");
   div.textContent = [...args].join(" ");
   document.body.appendChild(div);
}
<script src="https://twgljs.org/dist/2.x/twgl.min.js"></script>
<script type="foo" id="vs">
uniform mat4 sceneTransform;
uniform mat4 rotationTransform;
uniform vec2 objectPosition;
uniform vec2 objectScale;
attribute vec2 vertexPosition;
attribute vec2 vertexTexCoord;
varying vec2 UVs;
void main()
{
  UVs = vertexTexCoord;
  gl_Position = 
    sceneTransform *
    vec4( vertexPosition * objectScale + objectPosition, 0, 1);
}
</script>
<script type="foo" id="fs">
precision mediump float;

uniform vec3 objectColor;
uniform float objectOpacity;

void main()
{
 gl_FragColor = vec4(objectColor, objectOpacity);
}
</script>

When you call drawCircle you assign both of those attributes to buffers, then in your code above if you don't do something about the second attribute it's still pointing the previous buffer. That buffer is too small for your draw call and you get an error.

WebGL won't complain about unused attributes but it will complain about used attributes. You should always supply attributes your shader needs.

In your case you've got at least 2 options

  1. Change your code so your shader only uses one attribute

    You've got just one vertex shader if I read the code correctly. For those cases where your fragment shader isn't going to use the texture coordinates use a different vertex shader that doesn't supply them.

  2. Disable the attribute so that it uses a constant value

    gl.disableVertexAttribArray(...)
    

    means that that attribute will use a constant value supplied by

    gl.vertexAttribXXX
    

1 is arguably better than 2 because your vertex shader will not be wasting time reading from an attribute and copying it to a varying only not to use it in the fragment shader.

gman
  • 100,619
  • 31
  • 269
  • 393
  • Ah yes. This makes a lot of sense. I'm using one vertex shader for both the two-attribute rendering and the one-attribute rendering. It is keeping the vertex attribute binding when moving back to the one-attribute rendering. I will consider adjusting my code to use different vertex shaders depending on the circumstances. – Jonathan Heard Nov 30 '16 at 08:22
1

I saw this error when I was trying to recreate this and made a mistake. The tutorial code can be found here. However, I got this error because I defined color buffer AFTER setting the data in position buffer. I fixed it by defining color buffer , and then defining position buffer and binding data to the buffer. That did the trick. So in conclusion, this error can appear if we do not define attributes sequentially.

theusual
  • 741
  • 2
  • 11
  • 24
0

The error actually arises from different place, form a draw call from drawCircle function. By the looks of it you forgot to disable unused attribute arrays. Here you use just one attribute, which's bound to 0, but the error is for attribute 1. Evidently, you've enabled vertex array for attribute 1 somewhere and forgot to disable it. Now draw call verifies it's binding, founds that it's incorrect and arises GL_INVALID_OPERATION error. The spec says that out-of-bounds check should be performed only for attributes used in current program, but by the looks of it at least Chromium simply checks all array-enabled ones.

UPD. I've misunderstood Chromium's code. As pointed out by @gman, it indeed checks for out-of-bound accesses only attributes used by current program.

Kirill Dmitrenko
  • 3,474
  • 23
  • 31
  • This is great. Thank you Kirill! I imagine that this required some JS debugging to pinpoint. I appreciate your effort and will keep that option in mind for future debugging. The JS code looks beastly, but parallels the C++ source, so it's probably not as bad as it seems. – Jonathan Heard Nov 26 '16 at 07:15
  • SOLUTION: For the record, I resolved this by calling glDisableVertexAttribArray() on each attribute after every draw call is complete. The lesson here is that webgl, or at least emscripten, currently requires this extra step. – Jonathan Heard Nov 26 '16 at 07:19
  • @JonathanHeard You can also use [vertex array objects](https://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/) extension when it's available – Kirill Dmitrenko Nov 26 '16 at 07:47
  • Chromium does not check for unused attributes. There are tests for that in the conformance tests. [Example](https://jsfiddle.net/greggman/4sufyzar/). Also WebGL does **NOT** require disabling unused attributes. It never has – gman Nov 27 '16 at 05:12
  • As for vertex array objects [here's a polyfill that just works](https://github.com/greggman/oes-vertex-array-object-polyfill). Of course any user on a machine that doesn't support vertex array objects natively is probably also on a machine that's too slow for any webgl that really needs vertex array objects in the first place. – gman Nov 27 '16 at 05:23
0

I encountered this error because a vec2 attribute I was passing to a shader didn't contain data for each of my vertices...

duhaime
  • 25,611
  • 17
  • 169
  • 224