Capturing WebRender screenshots for debugging
When investigating a WebRender-specific rendering artifact (pixel snapping,
blending, compositing, tiling), the usual automated screenshot paths are not
useful: WebDriver, Marionette and the DevTools screenshot commands all capture
pixels by re-rendering the document through the software drawSnapshot
(CrossProcessPaint) path, which does not go through the WebRender
compositor. A screenshot taken that way can look correct even when the real
on-screen output is wrong.
The remote.screenshot.use_readback preference makes automated
screenshots read back the actual WebRender composited framebuffer instead, so
captured pixels match what is really on screen (including out-of-process
content).
Usage
Set
remote.screenshot.use_readbacktotrue(documented under the Remote Agent “Preferences” page).Take a screenshot the usual way:
WebDriver BiDi
browsingContext.captureScreenshotMarionette / WebDriver classic Take Screenshot
The
firefox-devtoolsMCPscreenshot_pagetool (which drives BiDi)
The returned image now reflects WebRender output. No other change to the client is required.
How it works
With the preference enabled, capture.canvas (remote/shared/Capture.sys.mjs)
draws the content area’s on-screen rectangle with
CanvasRenderingContext2D.drawWindow(..., DRAWWINDOW_USE_WIDGET_LAYERS) instead
of taking a drawSnapshot. In the parent process that flag drives a real
compositor readback (WebRenderLayerManager::MakeSnapshotIfRequired ->
WebRenderBridgeParent::RecvGetSnapshot -> WebRenderAPI::Readback ->
wr_renderer_readback), i.e. a glReadPixels of the composited framebuffer.
Limitations
Compositor readback can only return the pixels currently composited on screen, so this is a debugging aid, not a general-purpose screenshot mode:
Every capture degrades to the full content area of the foreground tab. Full-document (
origin: "document") screenshots, clip regions and element screenshots all return the viewport instead of the requested region.The target must be a content browsing context: drive the WebDriver session in content scope. A capture with no content area to read back, such as one taken while the session is in chrome (privileged) scope, keeps using the non-WebRender
drawSnapshotpath.Not supported on macOS. There the compositor runs in the GPU process and cannot read back the composited window itself, so it asks the parent process over
PNativeLayerRemote::RequestReadback. The parent only honours that request in automation (seeNativeLayerRootRemoteMacParent::RecvRequestReadback), and rejects it withIPC_FAIL, which crashes the parent process in a debug build and kills the GPU process in a release build.The capture reflects whatever is currently composited, so apply any zoom, scroll, or DOM change before capturing. Note that browser full-zoom is a chrome-scope operation and cannot be combined with a content-scope capture in a single step; CSS
zoomis content-drivable and exercises the same effective-zoom rendering path.Leave the preference at its default (
false) for normal automation.