7
$\begingroup$

Overview

My program receives some data points (0-400 per sec).

// simplified example of point's data structure
struct Point {
    QPoint p;
    QTime t;
    quint32 value;
}
  • I need to render some filtered points (eg. that point.time > currentTime-5 and point.value>5).
  • I need to store data history (up to ~50'000 points).
  • The data can be used also on the CPU.
  • I will use object-picking.
  • Points are rendered in 3d.
  • Camera position can change, but points data is constant.

Question

What is the best architecture to implement this kind of problem? What are best practices with this kind of data and what are practices to avoid?

Possible solutions

  • Storing and filtering data.
    1. Store the data on the CPU (world coordinates), filtering data on a CPU and sending filtered data to VBO, calculating screen-space coordinates with shaders.
    2. Store the data on a CPU and a GPU (update a VBO when some new point arrives).
      Filter points on CPU and calculate offsets and use glMultiDrawArrays.
    3. Store the data only on a GPU (update VBO when point arrives).
      Filter it with shaders, get data from VBO to CPU when needed.

Solution 1. is fairly easy, but I think it is a bad practice.
Solution 2. has data duplication, but it is easy to filter the data.
Solution 3. has data reading GPU->CPU.

I tried those solutions, and Solution 2. occurs to me as the best, but there are some issues with it:

  • Multiplication of data has to be handled gently.
  • It is not the easiest-to-read-and-modify solution.
  • When there are changes in data there is a lot of things to change.
    I used something like circular buffer on VBO and send offsets to render on it.

Environment

  • OpenGL 3.3
  • Qt 5+ bindings
  • Linux

PS
I am fairly new to computer graphics and openGL, so maybe this kind of problem even has its name. I changed my implementations few times, it took me a lot of time and I am not happy with it due to its complexity. Any materials, guides and thoughts are welcome.

$\endgroup$
4
$\begingroup$

My advice would actually be to stick with solution 1 unless and until something more is needed. It's the simplest and easiest to understand, and it should be quite feasible to make it perform well.

On the one hand, it may seem wasteful to re-filter the points and re-upload a bunch of data to the VBO every frame. I guess that this is why you say it is a "bad practice". But consider the total amount of data involved. An order-of-magnitude estimate shows that processing this amount of data every frame is not much work for a modern computer.

The Point struct you quote looks like it's about 20 bytes (assuming that p is 3 floats and t is one float or int). With 50,000 points, the total size of the data is about 1 MB. Even if your actual data struct is a bit larger, still the total size of the data is only a few MB—a small amount by today's standards. The time needed to process this amount of data on the CPU, and transfer it to the GPU, is on the order of ~0.1 ms. (Assuming ~20 GB/sec CPU bandwidth, and ~8 GB/sec PCIe transfer bandwidth.) So, this is an entirely reasonable amount of data to re-process each frame, and in fact you could handle many times more without stressing your system at all.

So, just keep things simple and efficient for memory access:

  1. Store the points in a big flat array (maybe using a ring buffer to allow new incoming points to overwrite old ones).
  2. Each frame, scan the points using a tight loop on the CPU, and copy the ones that pass the filter into the VBO.
    • For best efficiency, probably you should use GL_DYNAMIC_DRAW usage when you create the VBO, then use glMapBufferRange each frame and copy the filtered points directly into the mapped buffer.
    • Use GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT for the access parameter to glMapBufferRange; this will allow the GL driver to perform under-the-hood double-buffering to stream the data to the GPU efficiently. For more information on what's going on here, see Buffer Object Streaming.
  3. Then unmap the buffer and issue the drawing commands, using a vertex shader to transform the points to screen-space as usual.
$\endgroup$

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.