Using the update() and draw() loop properly in a multi-window application, while sharing some graphics between both windows

Hey Embers,

I could use some experience/thoughts/opinions on using Cinder’s update() and draw() calls in a multi-window application. I’m a little fuzzy on certain details even in a simple context. Therefore, I thought I’d describe some scenarios from simple to complex, and state how I think things are generally supposed to work along with some questions.

One useful fact as I understand it upfront:

Cinder calls update() once per frame, and calls draw() once per frame, per window.

Scenario 1: Single Window

  • update() - All CPU related code should ideally be run here.
  • draw() - All GPU related code should ideally be run here.

Question 1: It is my understanding that Cinder handles some graphics context matters around the draw() call, so even rendering graphics that will not immediately be shown on-screen (i.e. rendering something into a FrameBufferObject), should occur in draw(), is this true?

Scenario 2: Multi-window, no shared graphics

Pretty much the same approach as above, as there is no shared graphics between the two windows.

I’ve noticed that the Cinder sample entitled BasicAppMultiWindow makes use of setUserData() and getUserData(), to tie data context to a given window. In my case I instead bind separate draw(), resize(), close() functions to the two windows, and each drawWindowX() call simply access its own data. E.g. Window 1 connects to drawWindow1(), where as window 2 connects to drawWindow2().

Question2 : Are there any side-effects or problems with this approach? I.e. not using setUserData() and getUserData(), and instead just storing the individual window data in the application object.

Scenario 3: Multi-window, shared graphics

This is my current use case. Window 1 has a unique scene to render which includes a scaled down render of Window 2’s scene. Since textures can be easily shared across graphics contexts, my approach is as follows:

Window 2’s scene is rendered in the update() call, into a FrameBufferObject. In drawWindow1() window 1’s scene is rendered, which uses the color texture of the FrameBufferObject. In drawWindow2() basically only the color texture from the FrameBufferObject is, again, rendered in its full resolution.

Question 3: Is this a bad approach? Should Window 2’s scene ideally be rendered in drawWindow1()? If so, how do I ensure that drawWindow1() is always called before drawWindow2() for every frame?

Question 4: What if a scene needs to be rendered every n-th frame into a texture, where n is higher than 1 but still drawn on-screen every frame (via the texture). Should the rendering still ideally occur in draw() or should it occur in update(), and merely be drawn in draw()?

I hope my questions make sense, and maybe the answer is ultimately “Do whatever you want.”, but I feel like there are probably some best practices to observe when it comes to these things.

Any thoughts are welcome.

Thanks in advance,
Gazoo

Related links

Hi,

Here’s a few things I can tell you:

  • The update() method will be called once per frame, regardless of the number of windows you have.
  • The draw() method will be called once per window per frame (to find out which window, simply call getWindow() from within draw()).
  • When you create the window, a new OpenGL context is created. I believe this context will automatically be shared with the main context, but I could be wrong.
  • The shared context allows you to use the same texture in every window, but some OpenGL objects can not be shared, most notably buffer objects like Fbo and Vbo.

Scenario 2 is a good solution, I can’t think of any side effects.

Question 3: it could be that Cinder makes sure windows are drawn in the order they were created, but I am not sure. Performing rendering in update() isn’t necessarily bad, just know that you’ll be using the main GL context in that case.

Question 4: in that case, it’s probably not very important when the frame is rendered, or in which method. Either of them would work, I guess. Personally, I would try not to render in update() just to keep logic separate from draw calls, but that’s just preference.

~Paul

1 Like