WebDriver and Marionette screenshots normally re-render the document through the software drawSnapshot path, which does not reflect WebRender-specific rendering. When this pref is enabled, capture.canvas instead reads back the actual WebRender composited framebuffer via drawWindow with USE_WIDGET_LAYERS. Readback can only return the content area composited on screen, and its coordinates are relative to the chrome window rather than the document, so with the pref enabled every capture degrades to the whole content area of the foreground tab: full-document, clip and element captures all return the viewport. A capture with no content area to read back, such as one taken in chrome scope, keeps using drawSnapshot. Readback is not usable on macOS, where the GPU process asks the parent process for the composited pixels and the parent only honours that request in automation, so the documentation calls that platform out as unsupported and the marionette tests are skipped there. Differential Revision: https://phabricator.services.mozilla.com/D312921
3.2 KiB
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.captureScreenshot - Marionette / WebDriver classic Take Screenshot
- The
firefox-devtoolsMCPscreenshot_pagetool (which drives BiDi)
- WebDriver 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.