Amadeusz
Amadeusz

Reputation: 1706

glDrawArrays access violation writing location

I'm trying to visualize very large point cloud (700 mln points) and on glDrawArrays call debugger throws access violation writing location exception. I'm using the same code to render smaller clouds (100 mln) and everything works fine. I also have enough RAM memory (32GB) to store the data.

To store point cloud I'm using std::vector<Point3D<float>> where Point3D is

template <class T>
union Point3D
{
    T data[3];
        struct{
            T x;
            T y;
            T z;
        };
}

Vertex array and buffer initialization:

glBindVertexArray(pxCloudHeader.uiVBA);

glBindBuffer(GL_ARRAY_BUFFER, pxCloudHeader.xVBOs.uiVBO_XYZ);
glBufferData(GL_ARRAY_BUFFER, pxCloudHeader.iPointsCount * sizeof(GLfloat) * 3, &p3DfXYZ->data[0], GL_STREAM_DRAW);
glVertexAttribPointer((GLuint)0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);

glBindVertexArray(0);

Drawing call:

glBindVertexArray(pxCloudHeader.uiVBA);
glDrawArrays(GL_POINTS, 0, pxCloudHeader.iPointsCount); // here exception is thrown
glBindVertexArray(0);

I also checked if there was OpenGL error thrown but I haven't found any.

Upvotes: 3

Views: 973

Answers (2)

Andon M. Coleman
Andon M. Coleman

Reputation: 43319

I suspect your problem is due to the size of GLsizeiptr.

This is the data type used to represent sizes in OpenGL buffer objects, and it is typically 32-bit.

700 million vertices * 4-bytes per-component * 3-components = 8,400,000,000 bytes

There is a serious issue with trying to allocate that many bytes in GL if it is using 32-bit pointers:

8400000000 & 0xFFFFFFFF = 4,105,032,704 (half as many bytes as you actually need)

If sizeof (GLsizeiptr) on your implementation is 4 then you will have no choice but to split your array up. A 32-bit GLsizeiptr only allows you to store 4 contiguous GiB of memory, but you can work around this if you use 3 single-component arrays instead. Using a vertex shader you can reconstruct these 3 separate (small enough) arrays like so:

#version 330

layout (location = 0) in float x; // Vertex Attrib Ptr. 0
layout (location = 1) in float y; // Vertex Attrib Ptr. 1
layout (location = 2) in float z; // Vertex Attrib Ptr. 2

void main (void)
{
  gl_Position = vec4 (x,y,z,1.0);
}

Performance is going to be awful, but that is one way to approach the problem with minimal effort.


By the way, the amount of system memory here (32 GiB) is not your biggest issue. You should be thinking in terms of the amount of VRAM on your GPU because ideally buffer objects are designed to be stored on the GPU. Any part of the buffer object that is too large to be stored in GPU memory will have to be transferred over the PCIe (these days) bus when it is used.

Upvotes: 0

Reto Koradi
Reto Koradi

Reputation: 54592

You could draw the data in smaller batches. While there is no predefined upper limit for the size of a buffer, storing 8 GBytes of data in a single buffer is a lot. I'm not very surprised that something would blow up.

I would probably start with storing something like 1 million, or at most a few million, points in each buffer. Then use a pool of buffers with this fixed size, enough to accommodate all your data points.

This might even be beneficial for you performance, because it allows you start submitting draw calls before copying all your data into buffers. This will give you better overlap between CPU and GPU work.

With the amount of data you are shuffling around, you may also want to look into using glMapBuffer()/glUnmapBuffer() instead of glBufferData(). This generally avoids one copy operation for the data.

Upvotes: 0

Related Questions