Skip to content

Problems when rendering window one frame at a time #167

Open
@Boscop

Description

@Boscop

I'm using conrod & piston_window for the GUI in a VST plugin, but I'm having some problems with event handling:
The way the GUI is run in a VST is, one frame at a time.
So I can't use while let Some(event) = gui.window.next() { because that stalls, but when I try to do one frame by doing if let Some(event) = gui.window.next() { it doesn't register input events. So I tried while let Some(event) = gui.window.poll_event() { which seemed like a logical way to do it (only process events that are in the queue right now, don't block to wait for new events at the end of the queue), then I get input events, but no render events so the GUI doesn't render!
So what should I do to handle the GUI one frame at a time / without internal event loop?

Also, I have another question: What exactly does the event loop do behind the scenes (with the value given by window.set_ups())? Is there a way to turn the event loop off?

(This question is also relevant for writing game UIs, where the game should be in control of the loop and the GUI should be drawn over the scene.)


The host creates the window for each plugin and passes the handle to the plugin, then the plugin creates a child window with the given parent, and creates an opengl context on it.
I forked and modified conrod, piston_window, piston/window, glutin and glutin_window so I could pass the parent HWND down to where glutin creates the window. (I also modified glutin so that the child windows are created in the same thread and so that I could register timers on the window etc.).
Then I register a timer on that child window that calls my UI rendering callback repeatedly.
I already tested this before using only glutin and shaders to render my plugin GUI: https://i.imgur.com/Oi6JUzd.png

I created a standalone version of the plugin to test it, that passes NULL as the parent HWND so that glutin creates a normal window, and when I use while let Some(event) = gui.window.next() { it works correctly:

Input(Move(MouseRelative(1, 0)))
Update(UpdateArgs { dt: 0.016666666666666666 })
Idle(IdleArgs { dt: 0.000000000011982044 })
Input(Move(MouseCursor(411, 218)))
Input(Move(MouseRelative(1, 0)))
Render(RenderArgs { ext_dt: 0.000000000014581911, width: 682, height: 360, draw_
width: 682, draw_height: 360 })
AfterRender(AfterRenderArgs)
Input(Move(MouseCursor(412, 218)))
Input(Move(MouseRelative(1, 0)))
Input(Move(MouseCursor(414, 218)))
Input(Move(MouseRelative(2, 0)))
Input(Move(MouseCursor(415, 218)))
Input(Move(MouseRelative(1, 0)))
Update(UpdateArgs { dt: 0.016666666666666666 })
Render(RenderArgs { ext_dt: 0.000000000016664646, width: 682, height: 360, draw_
width: 682, draw_height: 360 })
AfterRender(AfterRenderArgs)
Input(Move(MouseCursor(416, 218)))
Input(Move(MouseRelative(1, 0)))
Update(UpdateArgs { dt: 0.016666666666666666 })

But it stalls (because of while) so I can't use this in the plugin version), but if I use if let Some(event) = gui.window.next() { on every frame, I only get Input events:

Input(Move(MouseRelative(0, -1)))
Input(Move(MouseCursor(46, 157)))
Input(Move(MouseRelative(0, -1)))
Input(Move(MouseCursor(46, 156)))
Input(Move(MouseRelative(0, -1)))
Input(Move(MouseCursor(46, 155)))
Input(Move(MouseRelative(0, -1)))
Input(Move(MouseCursor(46, 154)))
Input(Move(MouseRelative(0, -1)))
Input(Move(MouseCursor(46, 152)))
Input(Move(MouseRelative(0, -2)))
Input(Move(MouseCursor(46, 151)))
Input(Move(MouseRelative(0, -1)))
Input(Move(MouseCursor(46, 150)))
Input(Move(MouseRelative(0, -1)))
Input(Release(Mouse(Left)))

But I don't get render events, so nothing gets rendered.
My gui consists of a slider widget. I have it set up so that it prints 'value changed' when I change the slider with the mouse.
I wanted to see if the input is still being processed, even though nothing is rendered in this case.
But if I click on where the slider widget would be rendered, it doesn't print 'value changed' so the input apparently doesn't get processed correctly either.

What's weird is that I get mouse move events even if i dont move the mouse.
So I thought, maybe with 'while' I never get render events because for some reason it 1. puts input events before render events and 2. creates input events on every frame even when there is no input.
So I changed the code to this:

let mut i = 10;
while let Some(event) = gui.window.next() {
    i -= 1;
    if i == 0 {break}
    // usual event processing
}

Now it seems to work better: it renders and processes input, but it's very laggy and I'm not sure if this is the best way to do it.. And if there are no new events, it still waits until it got 10, which shouldn't happen. So my questions are:

  1. Is there a way to only process events that are in the queue right now, and not block to wait for new events at the end of the queue?
  2. How can I disable the event loop under the hood? I don't want it to interfere with my loop.
    Especially because the UI rendering is done in a callback triggered by a timer that fires every few ms.
  3. Currently ESC causes window.next() to return None. How can I ignore ESC? In the plugin, ESC shouldn't do anything, events should keep coming.

Edit: It seems the method of breaking out of the while loop after 10 events doesn't work in the plugin version (only in standalone, but laggy as hell). In the plugin version it renders but I get no input events. Why could that be?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions