Skip to content

Rethinking RedrawRequested #1041

@Osspial

Description

@Osspial

The current RedrawRequested event is currently hard to understand, which makes it difficult to implement and difficult to use. This issue will outline why I believe that is the case, and how I think we should fix it.

The current problems

RedrawRequested is extremely inconsistent with the rest of our API. This is exhibited in a couple ways:

  1. It's inconsistent with itself, since it sometimes provides ordering guarantees.
    • It's explicitly guaranteed to be the last event emitted if request_redraw is called, but isn't explicitly guaranteed to be the last event if the OS requested the redraw.
  2. It's inconsistent with WindowEvent, since no other window event provides any sort of ordering guarantees.
  3. It's inconsistent with EventsCleared, since it can be dispatched after EventsCleared if queued with request_redraw during the EventsCleared handler.

Some of that inconsistency is intentional (point 3), and some isn't (point 1), but the end result is that the API is difficult to explain and difficult to implement. Even beyond the complexity those inconsistencies introduce, the current model can result in duplication of RedrawRequested events when using the recommended event loop structure described in the root docs. Consider the following chain of events, with an application using the linked event loop structure:

  1. The user resizes the window, resulting in the OS triggering a RequestRedraw event.
  2. The event loop dispatches RequestRedraw between NewEvents and EventsCleared, as part of the standard control flow.
  3. EventsCleared gets dispatched, upon which the user updates the application's state and calls request_redraw.
  4. RequestRedraw gets dispatched immediately after EventsCleared.

The above situation has a couple problems: the first RedrawRequested is dispatched before the application has a chance to update its internal state, and the application goes through the expensive process of rendering a frame twice in a single iteration of the event loop. Ideally, our API wouldn't exhibit either issue, allowing the user to update their application state before all redraws occur and allowing Winit to unify redraw requests from the user and OS into a single event.

Proposed changes

  • Rename EventsCleared to MainEventsCleared
  • Remove RedrawRequested from WindowEvent.
  • Add RedrawRequested(WindowId) to Event.
    • Guarantee that RedrawRequested get dispatched between MainEventsCleared and RedrawEventsCleared.
  • Add RedrawEventsCleared to Event.

The rough event loop would end up behaving something like this (adapted from @icefoxen's example in #1032):

let event_loop_windows = /* event loop windows */;
let mut control_flow = ControlFlow::Poll; 

event_handler(NewEvents(StartCause::Init), &event_loop_windows, &mut control_flow);
while control_flow != ControlFlow::Exit {
    event_handler(NewEvents(/* get start cause */), &event_loop_windows, &mut control_flow);

    for e in (window events, user events, device events) {
        event_handler(e, &event_loop_windows, &mut control_flow);
    }
    event_handler(MainEventsCleared, &event_loop_windows, &mut control_flow);

    for window_id in (redraw_windows) {
        event_handler(RedrawRequested(window_id));
    }
    event_handler(RedrawEventsCleared, &event_loop_windows, &mut control_flow);

    /* wait or poll, as the control_flow dictates */
}
event_handler(LoopDestroyed, &event_loop_windows, &mut control_flow);

This results in numerous usability improvements over the current model:

  1. RedrawRequested's status as a special event is made more clear, as it's structurally distinct from standard window events.
  2. RedrawRequested is now guaranteed to be dispatched after event processing and application update code has been run, fitting it more naturally into the application update/render loop.
  3. Winit can now unify all OS-requested and user-requested redraw events, avoiding redundant redrawing.

Feedback is encouraged on the above designs, especially WRT naming - I'm not entirely happy with the names I've provided, and mainly included them because I can't think of anything better right now. There may be a better solution to the current API's problems, but I'm presently unable to think of one and we should certainly address them before 0.20's full release.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions