1
\$\begingroup\$

Recently,

I moved an app I am building from a computer with Intel & Nvidia to a computer with AMD CPU & GPU.

The problem is, that glDrawElements gives me bad access location and I can't figure out why.

The render function is the following:

template<class PositionData> void drawTexturedMesh(const PositionData &pd, const IndexedFaceMesh &mesh, const unsigned int offset, const float * const color, GLuint text)
{
  // draw mesh 
  const unsigned int *faces = mesh.getFaces().data();
  const unsigned int nFaces = mesh.numFaces();
  const Vector3r *vertexNormals = mesh.getVertexNormals().data();
  const Vector2r *uvs = mesh.getUVs().data();

  // Update our buffer data passed to GPU
  glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
  glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(double) * 3 * pd.size(), &pd.getPosition(0)[0]);
  glBindBuffer(GL_ARRAY_BUFFER, 0);

  glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
  glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(double) * 2 * mesh.getUVs().size(), &uvs[0][0]);
  glBindBuffer(GL_ARRAY_BUFFER, 0);

  glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
  glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(double) * 3 * mesh.getVertexNormals().size(), &vertexNormals[0][0]);
  glBindBuffer(GL_ARRAY_BUFFER, 0);

  // Binding element array and drawing. numFaces * 3 triangles per face.
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
  glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(unsigned int) * mesh.getFaces().size(), mesh.getFaces().data());

  glDrawElements(GL_TRIANGLES, 3 * nFaces, GL_UNSIGNED_INT, BUFFER_OFFSET(0));

  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}

Math, vectors and matrices are from Eigen. Any ideas or any other code part you would like to see? Bare in mind that this exact code works fine with nVidia.

Thanks.

\$\endgroup\$
  • 4
    \$\begingroup\$ Do you get any GL errors, when calling glGetError() just before glDrawElements()? \$\endgroup\$ – SurvivalMachine Jan 1 '17 at 15:16
  • 2
    \$\begingroup\$ Does it work if you use floats instead of doubles? \$\endgroup\$ – Maximus Minimus Jan 1 '17 at 15:41
  • \$\begingroup\$ Good morning, Le Comte du Merde-fou is correct. I spent a lot of time turning positions into floats and it worked. Why does that happen? Also I am afraid I have to use doubles... What can I do ? \$\endgroup\$ – Davelis4 Jan 2 '17 at 10:06
  • \$\begingroup\$ @LeComteduMerde-fou hey, care to reply? also post it as an answer so i can upvote it . thanks! \$\endgroup\$ – Davelis4 Jan 3 '17 at 18:04
  • \$\begingroup\$ @Davelis4 - been in work. Will add a reply & explanation of floats vs doubles shortly. \$\endgroup\$ – Maximus Minimus Jan 3 '17 at 20:10
4
\$\begingroup\$

This is caused by your use of double-precision.

In short, GPUs just don't like double-precision; the hardware support for it is just not there to the same degree as for CPUs.

A good (if slightly dated) overview may be read at http://arrayfire.com/explaining-fp64-performance-on-gpus/; I'll quote the opening paragraph because it explains things perfectly, but you can read it all if you're interested in further details.

GPUs are really good at doing math. The Achilles heel is when it comes to 64-bit double precision math. GPUs, at least consumer grade, are not built for high performance FP64. This is because they are targeted towards gamers and game developers, who do not really care about high precision compute. So vendors like NVIDIA and AMD do not cram FP64 compute cores in their GPUs.

So when you provide double-precision numbers to a GPU, one of the following may happen, depending on how old the GPU is, how good the drivers are, whether it's a consumer-grade or workstation GPU, and perhaps other factors:

  1. It will work in hardware but it will be slower.
  2. It will drop you back to software emulation (and be much slower).
  3. It won't work.

In the ideal world only (1) or (2) would happen, but driver quality being what it is, (3) is possible.

That's not to say that potentially bad AMD drivers are the actual cause in your case. If we review the documentation for glVertexAttribPointer, we'll see that there are actually two variants available:

  • glVertexAttribIPointer: use with integer data types.
  • glVertexAttribLPointer: use with double-precision.

It may very well be the case that simply switching to glVertexAttribLPointer is sufficient to resolve things for you (in which case NVIDIA and Intel are doing a format conversion that AMD is failing to do).

Where to go from here.

Review your use of doubles, because - despite what you might think - you almost certainly don't need them.

  • You are very unlikely to need doubles for your UVs, which will typically be in a 0..1 range. Single-precision will work.
  • You definitely don't need doubles for your normals, which are in the -1..1 range, unless you're doing something like passing unnormalized normals with crazy-high values.

That leaves positions, and unless you're drawing absolutely huge scenes, you don't need doubles for your positions either. In fact you'll probably run out of depth buffer precision before this becomes a factor (there are no 64-bit depth buffer formats on current hardware or APIs). A good overview is at http://www.gamasutra.com/view/feature/131393/a_realtime_procedural_universe_.php?print=1 and I'll quote:

...if the smallest unit you care about keeping track of is a millimeter, you start to lose accuracy around 1,000 km with floats and around 1 trillion km with doubles.

So single-precision is sufficient to represent scales of 1:1,000,000,000.

Conclusion

  • Review your use of doubles because you most probably don't need them. Typically using doubles is a good indicator that you've actually got a problem that may be better solved by switching to alternative algorithms.
  • If you absolutely must use doubles then ensure that you're using the correct API: glVertexAttribLPointer rather than glVertexAttribPointer.
  • Having the end result run unacceptably slow or just not work at all is something that you may just have to accept.
\$\endgroup\$
  • \$\begingroup\$ Thanks for the answer . Excellent explanation. Cheers!!! \$\endgroup\$ – Davelis4 Jan 3 '17 at 22:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.