OpenGL for EEG visualization

#1

Hi,

I am currently working on an EEG visualization software for my Bachelor thesis and I am using OpenGL. I don’t know how to plot data in real time.

My first idea was to plot all the data every 20ms but that’s not fast enough. How to draw only new data and then scroll left ?

Can anyone help me ? Thank you

#2

How close to “real time” does it need to be? How much data is it? How are you rendering it? How are you transferring the data to the GPU?

#3

The electronic device is sending data every 4ms (250Hz) to the computer. EEG data are stored in array buffer waiting to be plotted.

For a 20 seconds plot with 32 channels, I would say 160k points. I am currently drawing each point with the vertex function and then swap buffers.

This methods works well for a refreshing rate of 2 times per second but I need to have a smooth plot (30 FPS might be great ?!?). Is there a way to allocate memory for buffers and then dynamically change them and just “refresh” the plot ?

Thank you

#4

You mean glBegin/glVertex/glEnd? Don’t do that. Use glDrawArrays with the vertex data held in a vertex buffer object (VBO). The buffer needs to be over-sized so that you can add new data without overwriting the data used by the most recent frames.

There’s no need to dynamically change the size. Just allocate a buffer large enough for slightly over 20 seconds’ worth of data, used as a circular buffer. You only need to store the Y coordinate for each point; the X coordinate can be derived from gl_VertexID in the vertex shader. You can interleave the samples for the various channels and set the stride parameter accordingly in the glVertexAttribPointer calls.

#5

I am a beginner with OpenGL. As seen in a tutorial, I started to draw lines with glDrawArrays. That’s works fine with X and Y coordinate.

Your solution seems to be a great one but I don’t know how to work with the gl_VertexID to have the X coordinate ?

Can you please help me ? Thank you

#6

Find a book or tutorial which covers shaders (OpenGL Shading Language, aka GLSL).

#7

Does a circular buffer always need two calls ? ( 2x glDrawArrays, 2x glBufferSubData)

One from the offset to the end and one from the start to the offset - 1 ? Is there another solution ?

Thank you

#8

In general, yes. Although as the buffer will be slightly oversized, sometimes the second call won’t be needed (when the physical end of the buffer lies in the unused portion). Actually, for the glBufferSubData, one call will be the usual case. You’ll be drawing most of the buffer each frame but only updating a small portion. And if the number of samples added each time is fixed and the buffer size is an integer multiple of that number, the update will never straddle the physical end of the buffer.

A circular buffer will be the fastest solution.

#9

One caveat. A circular buffer implemented this way (with glBufferSubData or synchronizing glMapBuffer* may very well block in the driver waiting on the prior render call reading from this buffer to complete, particularly on mobile GPUs.

For more efficient ways to fill a circular ring buffer backed by a buffer object, see this wiki page:

In particular, PERSISTENT COHERENT buffer mappings and (as a fallback) UNSYNCHRONIZED buffer mapping.

#10

Bear in mind that page isn’t specifically about circular buffers. Much of it addresses the case where you’re replacing data in its entirety.

Ultimately, you’ll need to experiment to determine what’s efficient on your target system. Use debug output or client-side timings (using e.g. QueryPerformanceCounter on Windows or clock_gettime on Linux) to detect implicit synchronisation (i.e. if a function call blocks the CPU waiting for GPU operations to complete).

The only thing of which you can be certain is that any operation which requires synchronisation will result in synchronisation. Operations which don’t actually require it may still cause it. There are limits to how much effort the implementation will expend on determining whether synchronisation is actually necessary.

In theory, glDrawArrays is safer than glDrawElements, as the portion of the vertex arrays which is actually used is clear (the latter would have to scan the element array). Whether or not that helps you in practice is another matter.

One other caveat about line strips in circular buffers: the element at the end of the physical buffer must be duplicated at the start so you don’t get a gap between the two segments.

#11

Right. It’s about updating buffer objects efficiently.

(A buffer object just being a dumb container. You can apply any usage pattern you want to it, subject to the guidelines described. A circular buffer is one such usage pattern.)

In terms of the amount of text devoted, true. However, that’s not because replacing the buffer contents is the common case (it’s definitely not!), but instead because it’s the only part that requires special handling. “Update” doesn’t require any special steps.

In practical usage, “incremental update” is the common case. “Replacement” is the exception. And the reason is because full replacement requires synchronization or orphaning, which is more expensive, so you want to avoid that when possible.

(FWIW, I have also used the techniques described on this page to implement a circular buffer within a buffer object, multiple times in different engines, for transferring data efficiently from the CPU to the GPU, and this works very well in practice.)

Good advice. I’d second that.