Proper architecture for rendering history of streamed data (points)
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&mdash;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:


Store the points in a big flat array (maybe using a ring buffer to allow new incoming points to overwrite old ones).
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.

Then unmap the buffer and issue the drawing commands, using a vertex shader to transform the points to screen-space as usual.