2026-05-11
Stage 1: pixels on screen
The first thing rumb did was put a web page inside a GPUI window, and it did it the crudest way that would work.
Servo is built with an offscreen rendering context — a real GPU context made from the host's display handle, but with a surface nobody composites, so GPUI keeps owning the drawable the screen actually shows. Every render tick we spun Servo's event loop, told the WebView to paint, then called read_to_image on that offscreen framebuffer to pull the pixels back into system memory. Servo hands them over as RGBA and GPUI wants BGRA, so we swapped red and blue by hand across the whole buffer and fed the result to paint_image as if it were a picture someone had loaded off disk.
It was slow in exactly the way you would expect: a full round trip through the CPU, once a frame, for pixels that had never left the card. Frame pacing came from request_animation_frame alone and on-screen updates lagged around 1 Hz. Resize was not wired through. No input reached the page at all.
We knew all of that when we wrote it. The point was not to have a fast path, it was to find out whether Servo would embed in a GPUI app at all — whether the two event loops could share a process, whether the context juggling held, whether a page came out the other end. Once one did, the question stopped being "is this possible" and became "how do we stop the frame touching the CPU", which is a much better thing to be stuck on. The readback was never meant to survive contact with stage 2.