Threads, GL, RefCount - A few general questions


#1

Hello Jucers!

I have a few questions in general, and about everyone's favorite: Threads. :D

I have a system with 4 threads (Grapferuit JUCE):

  1. Message (of course)
  2. Process
  3. Consumer
  4. GL (with it's ThreadPool mechanism)

Now I run Processing at 60 FPS, and the Consumer thread waits. When a Processing cycle finishes, it ends up with a buffer of ints. I use double buffering, so swap them, wake up Consumer, and start the next Processing cycle. It works nicely. (Now I've just had the idea to change the Consumer stuff to a ThreadPool with a copy of the buffer, because consuming large buffers may takes more time than processing, and switching buffers too early leads to another problem. :D)

So the first question is about the GL stuff. I have different Objects, and they 'live' in the Processor. Some of them are Renderable, others are not. The problem is I can't render stuff based on the raw buffer data, but I'd like to let the renderable Object draw itself, when Processing is ready. It's a problem because the most important thing is to Process (create) and Consume (send) the data, GL is only for a visual feedback about what the user is actually doing. So I really don't want to wait with the processing until rendering finishes, but this way the next processing cycle changes my Objects during GL rendering. And it results visual glitches (first half of my Objects render their newly processed state, the other half renders their previous state.

Do you have any idea how to solve this? I was thinking about some caching mechanism, but it means I have to double all GL-related parameters, isn't it? These parameters usually really simple stuff (position, color, size, etc), but this kind of caching looks like a noobish way. :D

 

And now the other problem. As I wrote, I have objects, and all of them are subclasses of (let's say) Object. To let JUCE manage their lifetime, Object is subclass of ReferenceCountedObject, and all the Objects are sitting in a ReferenceCountedArray<Object> in Process. But some of them are Renderable also (with a virtual render() method). Renderable is also a ReferenceCountedObject, and all renderables are sitting in the GLComponent's ReferenceCountedArray<Renderable>. Ande here comes the problem, the compiler doesn't really like that eg. MyCustomRenderableObject inherits twice from a ReferenceCountedObject. (class MyCustomRenderableObject : public Object, public Renderable). What is the righta pproach in this situation? Maybe I overuse this refcount stuff? :)

As I see, for example Component is not ReferenceCountedObject (as I thought), and it holds it's child Components in a ScopedPointer<Array<Component*>>. Is it safe to use it this way? JUCE says to avoid raw pointers as much as possible. I'm confused...

 

Thank you for any replies!

Timo


#2

1. This is a general problem of audio processing: how to synchronize the GUI (or in your case OpenGL) with the processing thread. If the data is small, it's almost always best to just copy the data. If it is larger you will need something like a FIFO. You may want to look at Timur's great talk at CppCon on the subject.

2. The way you are inheriting your classes is called "The Diamond of Dread" and you should definitely avoid doing this. Instead Renderable should inherit from Object. Right?


#3

+1

Object is subclass of ReferenceCountedObject

Sounds to me like the wrong way around to structure your classes.


#4

Thank you for your answers, and the links!

 

1) I know, it's a common issue, and I've read a lot about it already, that's why I began with the double-buffer stuff on the data side. But that doesn't solve my problem on the rendering side. I think I've just had a little spontaneous enlightenment about how FIFOs, and AbstractFifo work.

Please correct me if I'm wrong, but I had an idea to write something like a GLCache class, and create one instance of it in every Object, so when the processing is done, I can 'save' my current state of the colors, positions, whatever in a FIFO-like way. After, during rendering then I can use the "first out" phase of the FIFO to pop the oldest values to the GL renderer. So for example in an ideal situation, I can use a GLCache with an array of cached values and the length of the hidden buffer array can be only 2 or 3 (for safety), and only read / write a single item per time? (Cause one frame looks like: [Process() -> Consume() & Render()])

 

2) I see the multiple inheritence everywhere in Juce, so that's why I've chosen this pattern. Ok, the Diamond of Dread problem is only in my case, but it's about to avoid leaks. Renderable cannot inherits from Object, because there are othr types, that can be renderable. For example a RenderableText. It has nothing to do with Object, but both of them has to be Renderble, and implement the "virtual void render() = 0" method. And they can register themselves in the GL Component as "Renderable", so the GL code doesn't need to search for Renderables with casting, it just iterates through it's renderables.

The reason I've made them also subclass of ReferenceCountedObject is to be lifetime-safe. But all of the possible renderable things are "owned" by the Process thread, or Objects (and Objects also "owned" by the Process), they usually constructed on the MessageThread and passed to Process, where I store them in a RefCountedArray.

Maybe Renderable should not be a subclass of ReferenceCountedObject, and I should store them in an "Array<Renderable*>" on the GL side? Or even just like in Component: "ScopedPointer<Array<Renderable*>>"


#5

Ah, and maybe not every Texts and Objects are Renderable. Like not every Component is a ButtonListener, but anything can be a ButtonListener. To be honest, the real problem is I don't completely understand RefCounting, and when should I use it... :)

Eg.: Returning a raw pointer of refcounted object is ok, but storing a raw pointer of it as class member is not ok... 


#6

Just to not leave this thread hanging, I came up with solutions for both problems.

1) I gave a virtual cache() method to my Renderable class, so any subclass of it can handle it's own data caching. It works nicely, no visual glitches, but it assumes that triggering of a GL render can happen only when Processing (so caching also) is done. In my case it's fine.

2) Renderable (and a similar class Selectable) now don't inherit from ReferenceCountedObject, and I've changed the type of a few Arrays according to these changes, and it works fine. I think I need more practice on this topic, but now I can see the light at the end of the tunnel... :d

Thanks for the tips!