T-517/T-511 review fixes: the express lane's own-slot base guarded, slots invalid while written, WebGL off after a give-up (cavekit-gpu-rendering R3, R8, R11)

An inspector review of the day's GPU-path work (context/impl/impl-review-findings-gpu.md), every
finding fixed:
- the express lane builds on its slot's own frame only with no main-lane job in flight (a composed,
  unpolled job's change was in no slot's record), never takes a slot a main-lane job holds, and a slot
  is invalid from a job's post (either lane) to its publish;
- an own-slot base after a scroll only when neither the latest composite nor the slot's frame had
  fixed or stuck layers (the engine judges those against the previous composite);
- a failed main-lane job drops every slot's record; a recentring move invalidates the slot first;
- engine 0054: a window-moved shift saves every kept rect before writing any, and WebGL makes no EGL
  contexts once the daemon has given the GPU path up (jihad_offscreen_gpu_given_up);
- the adapter reads the window trailer once.
T-504: a zoom's express frame gives up main-lane frames at the old zoom that were not composed yet.

The day's failed approaches are in context/impl/dead-ends.md. Rounds 339-340: the default checks of
all three interfaces pass; verify mode on a main-lane Wikipedia pan, 0 structurally wrong frames.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J67ZH8aYy8uzhgZDZ6Nvx9
This commit is contained in:
NotAlexNoyleandClaude Opus 5.5 committed 2026-10-19 11:13:45 -04:00
1 parent 20ccf6b553
commit 902187aebe
11 files changed
+264 -10

No files matched your search

@@ -0,0 +1,141 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NotAlexNoyle <alexnoyle@icloud.com>
Date: Mon, 19 Oct 2026 11:12:42 -0400
Subject: [PATCH] gpu compositor: kept rects all saved before any is written;
WebGL off once the GPU path is given up
Review of the T-511/T-517 work. With the daemon's window move (flag 8) the kept rects were copied
one at a time, each saved and written before the next was read: a rect's old rows can be another's
new ones (two stacked sticky bars), which then got the other bar's pixels. All are saved first now,
then all written, as the in-place shift does.
jihad_offscreen_gpu_given_up() (the daemon calls it when it gives the GPU path up) and
jihad_gpu_compositor_on(), which WebGL's EGL choice and the headless GfxInfo now ask: a daemon that
fell back to the software path after a GPU stall made new WebGL contexts on the abandoned GPU. The
environment is left as it is (other threads read it).
---
dom/canvas/WebGLContext.cpp | 7 +++++--
gfx/layers/opengl/CompositorOGL.cpp | 23 ++++++++++++++++++-----
widget/PuppetWidget.cpp | 16 ++++++++++++++++
widget/headless/GfxInfo.cpp | 8 +++++---
4 files changed, 44 insertions(+), 10 deletions(-)
diff --git a/dom/canvas/WebGLContext.cpp b/dom/canvas/WebGLContext.cpp
index cf33cbe5a6..3055fb7fd3 100644
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -80,6 +80,8 @@
#include "mozilla/dom/WebGLRenderingContextBinding.h"
+extern "C" bool jihad_gpu_compositor_on(); // Jihad T-511: widget/PuppetWidget.cpp
+
namespace mozilla {
using namespace mozilla::dom;
@@ -714,8 +716,9 @@ WebGLContext::CreateAndInitGL(bool forceEnabled,
// Jihad (T-511): on the GPU path (JIHAD_GPU_COMPOSITOR) the headless build's default GL provider
// is the null one; the device's GPU is reached through EGL, as the compositor reaches it. On the
- // software path the default provider fails and WebGL stays off.
- const bool useEGL = PR_GetEnv("MOZ_WEBGL_FORCE_EGL") || PR_GetEnv("JIHAD_GPU_COMPOSITOR");
+ // software path, and once the daemon has given the GPU path up, the default provider fails and
+ // WebGL stays off.
+ const bool useEGL = PR_GetEnv("MOZ_WEBGL_FORCE_EGL") || jihad_gpu_compositor_on();
bool tryNativeGL = true;
bool tryANGLE = false;
diff --git a/gfx/layers/opengl/CompositorOGL.cpp b/gfx/layers/opengl/CompositorOGL.cpp
index 8510eed11a..4eb3ccf391 100644
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -2174,18 +2174,31 @@ JihadShiftRows(JihadFrameRequest* aReq)
// Only the kept rects (still layers) are copied, from where they were: dy rows back, the slot's
// frame's own rows
if (aReq->windowMoved) {
+ // every rect saved before any is written: a rect's old rows may be another rect's new ones
+ // (two stacked bars), and the rows read and written may overlap
static uint8_t* sKept = nullptr;
static size_t sKeptCap = 0;
const size_t stride = (size_t)aReq->stride;
+ size_t total = 0;
+ for (int k = 0; k < aReq->nKeep; k++) {
+ int x0, x1, y0, y1;
+ if (JihadKeepRect(aReq, k, &x0, &x1, &y0, &y1)) total += (size_t)(x1 - x0) * 4 * (y1 - y0);
+ }
+ if (!total) return;
+ if (total > sKeptCap) { free(sKept); sKept = (uint8_t*)malloc(total); sKeptCap = sKept ? total : 0; }
+ if (!sKept) return;
+ uint8_t* p = sKept;
for (int k = 0; k < aReq->nKeep; k++) {
int x0, x1, y0, y1;
if (!JihadKeepRect(aReq, k, &x0, &x1, &y0, &y1)) continue;
- const size_t rowBytes = (size_t)(x1 - x0) * 4, bytes = rowBytes * (y1 - y0);
- if (bytes > sKeptCap) { free(sKept); sKept = (uint8_t*)malloc(bytes); sKeptCap = sKept ? bytes : 0; }
- if (!sKept) return;
- uint8_t* p = sKept; // saved first: the rows read and written may overlap
+ const size_t rowBytes = (size_t)(x1 - x0) * 4;
for (int r = y0; r < y1; r++, p += rowBytes) memcpy(p, aReq->dest + ((ptrdiff_t)r - dy) * (ptrdiff_t)stride + (size_t)x0 * 4, rowBytes);
- p = sKept;
+ }
+ p = sKept;
+ for (int k = 0; k < aReq->nKeep; k++) {
+ int x0, x1, y0, y1;
+ if (!JihadKeepRect(aReq, k, &x0, &x1, &y0, &y1)) continue;
+ const size_t rowBytes = (size_t)(x1 - x0) * 4;
for (int r = y0; r < y1; r++, p += rowBytes) memcpy(aReq->dest + (size_t)r * stride + (size_t)x0 * 4, p, rowBytes);
}
return;
diff --git a/widget/PuppetWidget.cpp b/widget/PuppetWidget.cpp
index cf3fa4cea9..ba586db982 100644
--- a/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -1436,6 +1436,22 @@ jihad_offscreen_paint_layers(nsIWidget* aWidget, nsIDocShell* aDocShell)
{
return (aWidget && static_cast<PuppetWidget*>(aWidget)->JihadPaintLayers(aDocShell)) ? 1 : 0;
}
+// T-511 (review): the daemon gave the GPU path up (a stall past its bound, refused jobs): WebGL
+// makes no more EGL contexts on that GPU. jihad_gpu_compositor_on() is what the engine's GPU-path
+// checks ask (JIHAD_GPU_COMPOSITOR in the environment, and not given up); the environment is not
+// changed, other threads read it.
+static mozilla::Atomic<bool> sJihadGpuGivenUp(false);
+MOZ_EXPORT void
+jihad_offscreen_gpu_given_up()
+{
+ sJihadGpuGivenUp = true;
+}
+MOZ_EXPORT bool
+jihad_gpu_compositor_on()
+{
+ static const bool sEnv = getenv("JIHAD_GPU_COMPOSITOR") != nullptr;
+ return sEnv && !sJihadGpuGivenUp;
+}
MOZ_EXPORT int
jihad_offscreen_compose_peek(nsIWidget* aWidget, uint32_t aToken)
{
diff --git a/widget/headless/GfxInfo.cpp b/widget/headless/GfxInfo.cpp
index 1c25a0ccda..81e70f0b6b 100644
--- a/widget/headless/GfxInfo.cpp
+++ b/widget/headless/GfxInfo.cpp
@@ -7,6 +7,8 @@
#include "nsUnicharUtils.h"
#include <stdlib.h> // Jihad T-511: getenv
+extern "C" bool jihad_gpu_compositor_on(); // Jihad T-511: widget/PuppetWidget.cpp (the GPU path on, not given up)
+
namespace mozilla {
namespace widget {
@@ -63,9 +65,9 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
if (aOS) {
*aOS = OperatingSystem::Linux;
}
- // Jihad (T-511): on the GPU path (JIHAD_GPU_COMPOSITOR: the device's GPU through EGL) WebGL's
- // OpenGL context is allowed; UNKNOWN reads as blocklisted to WebGL
- if (aFeature == nsIGfxInfo::FEATURE_WEBGL_OPENGL && getenv("JIHAD_GPU_COMPOSITOR")) {
+ // Jihad (T-511): on the GPU path (JIHAD_GPU_COMPOSITOR: the device's GPU through EGL, and the
+ // daemon has not given it up) WebGL's OpenGL context is allowed; UNKNOWN reads as blocklisted
+ if (aFeature == nsIGfxInfo::FEATURE_WEBGL_OPENGL && jihad_gpu_compositor_on()) {
*aStatus = nsIGfxInfo::FEATURE_STATUS_OK;
}
// Not blocklisted, not accelerated: leave the decision to the caller's
+1 -1
View File
@@ -9,7 +9,7 @@ UXP_BASE=5f290d1097a8350b56ce2092aa9d5fc9147c6661
# The tip of our engine branch (NotAlexNoyle/UXP-Jihad, jihad/uxp-<UXP_TAG>: one commit per patch
# in build/desktop/patches) the build uses. The third_party/uxp gitlink and the OE recipe's SRCREV
# must both be this commit.
UXP_PIN=a563423cac6d894d60434c39fc3097cadcb5c641
UXP_PIN=66180dfea4ac5206d2c8da697fddb8c44d2a38d7
# What the user agent and the start pages say: Goanna version and the short UPSTREAM base.
GOANNA_VERSION=6.9
UXP_SHORT=5f290d1
@@ -34,7 +34,7 @@ COMPATIBLE_MACHINE = "(tenderloin|opal)"
# for real (build/engine/patch-queue.sh --check proves it reproduces SRCREV's tree):
# SRC_URI = "git://repo.palemoon.org/MoonchildProductions/UXP.git;protocol=https;tag=<UXP_TAG>"
SRC_URI = "git://${JIHAD_REPO}/third_party/uxp;protocol=file;nobranch=1"
SRCREV = "a563423cac6d894d60434c39fc3097cadcb5c641"
SRCREV = "66180dfea4ac5206d2c8da697fddb8c44d2a38d7"
S = "${WORKDIR}/git"
# Modern C++14 toolchain (device-build R1) — the stock dylan/device gcc cannot build UXP.
+29
View File
@@ -255,3 +255,32 @@ the pool directory listing shows every one. The CDN in front of archive.debian.o
clone" had never been true. Both Debian fetchers now try up to five times with a fresh cache-buster
and a short back-off; sha256 still decides what is accepted. Do not pin a mirror instead: the pool
paths are the record.
## The GPU path's frame cost: what did not pay (2026-10-19, T-511, T-517, T-504)
Measured on the bench and reverted; the rounds are in `docs/rounds/t502-2026-10-17/` (`webgl-r281-313.txt`,
`floor-r314-320.txt`).
- **The kept rows' shift on a worker thread** while the band passes compose (r315): no gain. Both cores
are full during a pan (the daemon ~1.5 cores, WebAppMgr and an outside `sensord` ~18 % each); the copy
only competed and took 6-19 ms on the worker. The frame windows removed the copy instead.
- **Every frame by the whole path** (`JIHAD_GPU_NO_BAND_TARGET=1`, r318): jobs median 67 ms against 41.5.
- **One main-thread notification per band frame** (DidComposite only from the first pass, r320): no change.
- **A taller band surface** for the WebGL cube (512/640 rows, r300) or for Wikipedia's chunks (600/942, round
265): passes got cheaper in number and dearer each; no gain on the card.
- **Slices at natural x** for Wikipedia's two sticky columns: they do not fit a 256-row band surface (the
pair spans 717 px and 542 rows), and a taller surface is the case above.
- **See-through sticky columns kept once proven still** (r333): on Wikipedia it applied to one 13-row sliver
(the contents panel's highlight follows the reading position, so it changes during a pan), and the
per-frame save and compare slowed frames (median gap 71 ms against 42).
- **The express lane on its own slot for pages with fixed or stuck layers**: correct only if the engine
knew the base is older than the previous composite (the review's finding 3); now taken only without them.
- **A GPU swizzle by copying the drawn rows into a texture** (`glCopyTexSubImage2D`, r338): the copy waits
for the composite to resolve, 110-358 ms; double taps got worse (up to 499 ms). The old pass mode
(`JIHAD_GPU_PASS=1`, a texture window target sampled directly) helps the zoom's whole frame (r336: every
tap under 190 ms) but samples the frame target during band passes too (wrong band frames) and slows a
simple page's pans (median 29 against 26 ms): not usable as it stands.
- **Reading a WebGL canvas's frame from its texture framebuffer**: 8-9 fps; through a canvas-sized pbuffer
16-17; shared textures 21.5-25. **An EGLImage from a GL texture**: the driver refuses it
(`EGL_BAD_PARAMETER`, gpu-bench.c section 17).
- Measured, worth having later: **the adapter invalidating only the changed strip** (r304): a canvas
animation +12 % on the card; needs the changed rect in the frame trailer.
+36
View File
@@ -0,0 +1,36 @@
---
created: "2026-10-19"
last_edited: "2026-10-19"
---
# Review — the GPU path's T-511 and T-517 work (2026-10-19)
Build site: context/plans/build-site.md
`ck:inspector`, read-only, on the main-repo commits `bf26d622` (YapProxy locks), `8518970c` (T-511:
WebGL, the column read), `e0416cbc` (no cache clean on write-through segments) and `4b8a595b` (frame
windows), with the engine commits they pin (`6ea0ae9f54`, `8e117bab07`, `a563423cac`). An interim
review ahead of T-513 (Tier 87's review, Codex by direct invocation with the inspector as backup):
the frame windows reach into the adapter (LunaSysMgr's process) and change how both frame lanes
keep their records, so they were reviewed before more work went on top.
| # | Severity | Where | Finding | Disposition |
|---|---|---|---|---|
| 1 | P1 wrong pixels, a race | `BrowserPageGoanna.cpp` `xStart` (the express lane's own-slot path, its slot choice) | The express lane built on its slot's own frame while a main-lane job was composed but not yet polled (its change not yet in any slot's record), and could pick the slot a main-lane job was still being read into (the slot still marked valid from its older publish; a recentring move there would race the read) | Fixed: the own path only with no main-lane job in flight; the express lane never takes a slot a main-lane job holds; a slot is marked invalid when a job (either lane) is posted into it, and its publish records it again |
| 2 | P1 wrong pixels | engine `CompositorOGL.cpp` `JihadShiftRows`, the window-moved branch | Kept rects copied one at a time (saved, then written, before the next was read): stacked sticky bars got each other's rows | Fixed (engine 0054): every rect saved first, then all written |
| 3 | P1/P2 likely wrong pixels | own-slot bases, both lanes | The engine judges kept rects and fixed columns against the previous composite; an own-slot base is older, so a sticky layer that stuck between them (no tree change) could leave a ghost | Fixed by narrowing: an own-slot base after a scroll only when neither the latest composite nor the slot's own frame had fixed or stuck layers (`mGpuSlotFixed`). Pages with a fixed header go back to the copy from the newest slot (the long test page's median frame gap 26 -> 30 ms) |
| 4 | P2 wrong pixels | main lane, a job that fails after composing | Only the active slot was invalidated; the other slots' records missed the composite's change | Fixed: `gpuSlotsLost()` on any failed main-lane job |
| 5 | P2 | recentring before a post that fails | The rows moved, the slot stayed valid with its old window | Fixed: the slot is marked invalid before the move (both lanes) |
| 6 | P2 | WebGL after the daemon gives the GPU path up | `JIHAD_GPU_COMPOSITOR` stays set, so WebGL went on making EGL contexts on an abandoned GPU | Fixed (engine 0054): `jihad_offscreen_gpu_given_up()` from the daemon's fallback; WebGL's EGL choice and the headless GfxInfo ask `jihad_gpu_compositor_on()` |
| 7 | trivial | adapter `BrowserOffscreen::windowBytes()` | The trailer's window read twice | Fixed: read once (the compiled adapter came out the same) |
Found correct by the review: the YapProxy lock pairing and tag counter; the header at each segment's
start; no frame rows reaching the trailer on any budget (software path, overscan, express lane);
the adapter's bounds and fallback to window 0; no software repaint on a windowed GPU frame; the
recentring moves inside the segment; the window-moved shift's source rows; the column read's stale
columns; the express publish's records; the stale bookkeeping's failure paths; WebGL's share root,
context release and pbuffer readback.
Validation after the fixes (rounds 339-340): the default checks of Enyo, Mochi and Mojo pass;
verify mode on a main-lane Wikipedia pan, 30 frames after the pan began, 0 structurally wrong;
Wikipedia's express pan median frame gap 43 ms (unchanged).
+2 -1
View File
@@ -13,7 +13,7 @@ available is met.
`RB_20261006` (`5f290d1097a8350b56ce2092aa9d5fc9147c6661`, 2026-10-04) — `UXP_TAG` / `UXP_BASE` in
`build/engine/engine-version.env`. (Until 2026-10-12: master `b2594a4`, 2026-06-30.)
- **Our modified engine:** the branch `jihad/uxp-RB_20261006` of the fork `NotAlexNoyle/UXP-Jihad`,
at `UXP_PIN` (`a563423cac6d894d60434c39fc3097cadcb5c641`), present in this repository as the
at `UXP_PIN` (`66180dfea4ac5206d2c8da697fddb8c44d2a38d7`), present in this repository as the
submodule `third_party/uxp`. The device build compiles that tree.
- **The modifications as patches:** `build/desktop/patches/` holds every change as a patch
against `UXP_BASE`. `build/engine/patch-queue.sh --check` proves the patches, applied in
@@ -77,6 +77,7 @@ modifications; the same changes are the commits of `jihad/uxp-RB_20261006`, one
| `0049-layers-a-composite-acknowledged-after-the-widget-is-.patch` | `gfx/layers/client/ClientLayerManager.cpp` | T-515: `DidComposite` returns when the widget is gone (a card closed while a compose job it had asked for was waited out; its acknowledgement reached the main thread after the page was torn down: SIGSEGV at 0) |
| `0050-gpu-compositor-every-compose-job-compose-and-read-on.patch` | `widget/PuppetWidget.cpp`, `gfx/layers/ipc/CompositorBridgeParent.cpp`, `gfx/layers/opengl/CompositorOGL.cpp` | T-516: with `JIHAD_GPU_TIMELINE=1` every compose job, compose and read logged with a timestamp on the daemon's clock (otherwise sampled), so a slow frame's time can be placed |
| `0051-webgl-contexts-on-the-device-s-GPU-on-the-GPU-path-s.patch` | `dom/canvas/WebGLContext.cpp`, `dom/canvas/WebGLContextValidate.cpp`, `widget/headless/GfxInfo.cpp`, `gfx/gl/GLContextEGL.h`, `gfx/gl/GLContextProviderEGL.cpp`, `gfx/gl/SharedSurface.cpp`, `gfx/layers/client/ClientCanvasLayer.cpp` | T-511: WebGL on the GPU path asks EGL for its context (the headless default provider is null) and is not blocklisted there (headless GfxInfo); the Adreno 220's `GL_INVALID_ENUM` for `GL_STENCIL_BACK_WRITEMASK` queried in its own error scope; every pbuffer context on the GPU path in one share group under a 1x1 root, a WebGL canvas handing the compositor its texture (`SurfaceFactory_GLTexture`, zero copy; `JIHAD_GPU_NO_SHARE=1`: unshared, read back through a canvas-sized pbuffer); a context released from its thread before it is destroyed (the next teardown faulted in libGLESv2) |
| `0054-gpu-compositor-kept-rects-all-saved-before-any-is-wr.patch` | `gfx/layers/opengl/CompositorOGL.cpp`, `widget/PuppetWidget.cpp`, `dom/canvas/WebGLContext.cpp`, `widget/headless/GfxInfo.cpp` | T-517/T-511 review: a window-moved shift saves every kept rect before writing any; `jihad_offscreen_gpu_given_up()` / `jihad_gpu_compositor_on()`, so WebGL makes no EGL contexts once the daemon has given the GPU path up |
| `0053-gpu-compositor-a-shift-that-is-the-daemon-s-window-m.patch` | `gfx/layers/opengl/CompositorOGL.{h,cpp}`, `widget/PuppetWidget.cpp` | T-517: a compose job's shift may be the daemon's move of the frame's window in its segment (flag 8): the rows are in place, only the kept rects are copied from where they were |
| `0052-gpu-compositor-the-frame-s-bands-read-as-one-column-.patch` | `widget/PuppetWidget.{h,cpp}`, `gfx/layers/ipc/CompositorBridgeParent.cpp`, `gfx/layers/opengl/CompositorOGL.{h,cpp}` | T-511: a compose job may give its bands' stale columns (flag 4); with no shift, kept rows or fixed columns, the bands are read as the one column the change and they span when it is under three quarters of the width; the poll reports the change's columns (twelve ints); `JIHAD_GPU_NO_COLUMN_READ=1` reads whole rows |
+6 -3
View File
@@ -183,12 +183,15 @@ QImage BrowserOffscreen::surface()
size_t BrowserOffscreen::windowBytes() const
{
JihadFrameTrailer* t = jihadFrameTrailer((unsigned char*)m_ipcBuffer->buffer(), m_ipcBuffer->size());
if (!t || t->magic != kJihadWindowMagic || t->windowRows <= 0 || m_header->renderedWidth <= 0 || m_header->renderedHeight <= 0)
if (!t || t->magic != kJihadWindowMagic)
return 0;
const int rows = t->windowRows; // read once: the checks and the offset are of the same value
if (rows <= 0 || m_header->renderedWidth <= 0 || m_header->renderedHeight <= 0)
return 0;
const size_t rowBytes = (size_t)m_header->renderedWidth * 4;
if ((size_t)(t->windowRows + m_header->renderedHeight) * rowBytes > (size_t)rasterSize())
if ((size_t)(rows + m_header->renderedHeight) * rowBytes > (size_t)rasterSize())
return 0;
return (size_t)t->windowRows * rowBytes;
return (size_t)rows * rowBytes;
}
void BrowserOffscreen::resetBuffer()
+42 -3
View File
@@ -1198,6 +1198,7 @@ void MarkInputLatency() { gJihadInputMarkMs = jihadNowMs(); }
void BrowserPageGoanna::gpuFallBack(const char* why) {
if (!mPage || !mPage->GpuPath()) return;
FrameLockGuard frameLock; // T-515
if (jihad_offscreen_gpu_given_up) jihad_offscreen_gpu_given_up(); // T-511 (review): WebGL off too
xInvalidate();
for (const GpuJob& j : mGpuJobs) mPage->ComposeForget(j.token);
mGpuJobs.clear();
@@ -3736,9 +3737,11 @@ bool BrowserPageGoanna::gpuJobStart(int slot, int key, unsigned char* pixels, si
ownStale - std::min(ownStale, recentInv) < h / 3;
// T-517: after a scroll too, when the slot's window can move by it: no copy, the entering rows and
// the slot's stale rows read
// (review) not with fixed or stuck layers, now or in the slot's frame: the engine judges kept rects
// and fixed columns against the previous composite, and this base is older
const bool ownWin = winOn && slot >= 0 && mGpuSlotValid[slot] && mGpuSlotX[slot] == x && mGpuSlotZoom[slot] == zoom &&
mGpuSlotW[slot] == w && mGpuSlotH[slot] == h && mGpuSlotY[slot] != y && labs((long)y - mGpuSlotY[slot]) < h / 2 &&
ownStale - std::min(ownStale, recentInv) < h / 3;
ownStale - std::min(ownStale, recentInv) < h / 3 && mGpuFixedHi <= mGpuFixedLo && !mGpuSlotFixed[slot];
if (!src && !sWhole && !sNoLatest && !ownCheap && !ownWin && L >= 0 && L != slot && L < 3 && mGpuSlotValid[L] && mGpuSlotX[L] == x &&
mGpuSlotZoom[L] == zoom && mGpuSlotW[L] == w && mGpuSlotH[L] == h &&
mGpuStaleHi[L] <= mGpuStaleLo[L] && mGpuStale2Hi[L] <= mGpuStale2Lo[L] && mGpuFixedHi >= mGpuFixedLo) {
@@ -3788,6 +3791,7 @@ bool BrowserPageGoanna::gpuJobStart(int slot, int key, unsigned char* pixels, si
if (nw < 0 || nw + h > capRows) {
const long to = (long)winCentre - dy;
if (to >= 0 && to + h <= capRows) {
mGpuSlotValid[slot] = false; // (review) its rows move: no record of them holds until the publish
memmove(pixels + (size_t)to * stride, pixels + (size_t)mGpuSlotWin[slot] * stride, (size_t)h * stride);
nw = winCentre;
} else {
@@ -3864,6 +3868,7 @@ bool BrowserPageGoanna::gpuJobStart(int slot, int key, unsigned char* pixels, si
? mPage->ComposePost(winPixels, winBytes, h, sLo, sHi, s2Lo, s2Hi, shift, src, srcYForEngine, baseIsPrevious, nullptr, colX0, colX1, winMoved)
: mPage->ComposeStart(winPixels, winBytes, sLo, sHi, s2Lo, s2Hi, shift, src, srcYForEngine, baseIsPrevious, false, colX0, colX1, winMoved);
if (token) mXLastPostToken = token;
if (token && slot >= 0) mGpuSlotValid[slot] = false; // T-517 (review): being written; its publish records it again
if (!token) {
// T-509: a compositor that takes no job (no GL context: EGL failed) — three in a row and the
// daemon renders in software for good
@@ -4075,11 +4080,37 @@ bool BrowserPageGoanna::xStart(int64_t now, double Z, const XWant& want, int64_t
if (!S.keys[t] || !S.base[t] || S.seg[t] < need) continue;
if (mInFlight[t] && now - mPaintMs[t] < 2000) continue;
if (t == mXMainSlot || (base && t == b) || (mXJob.on && t == mXJob.slot)) continue;
bool mainJob = false; // T-517 (review): a main-lane frame is still composed or read into it
for (const GpuJob& gj : mGpuJobs) if (gj.slot == t) mainJob = true;
if (mainJob) continue;
if (S.keys[t] == mActiveKey && (pass == 0 || mXMainWindow)) continue;
slot = t;
}
}
if (slot < 0) return true; // a return frees one (the hook kicks)
// T-504: a frame at a new zoom goes ahead of the main lane's frames at the old one that have not
// been composed yet: they would be dropped at their poll once this one is out, and the compositor
// thread takes jobs in order (on Wikipedia after its load the first double tap's frame waited
// 144 ms behind one). Not a failure of theirs; their slots' records go (one may have begun)
if (!aBase && !mGpuJobs.empty()) {
int superseded = 0;
for (size_t k = 0; k < mGpuJobs.size();) {
if (mGpuJobs[k].zoom != Z && mPage->ComposePeek(mGpuJobs[k].token) == 0) {
mPage->ComposeForget(mGpuJobs[k].token);
mGpuJobs.erase(mGpuJobs.begin() + k);
++superseded;
} else {
++k;
}
}
if (superseded) {
gpuSlotsLost();
mNeedsPaint = true; // the main lane's own frame at the new zoom, later
static uint32_t sSupN = 0;
if (sSupN++ < 20 || (sSupN % 50) == 0)
fprintf(stderr, "[jihad-gpu] express frame at zoom %.4f goes first: %d main-lane frame(s) at the old zoom not yet composed given up\n", Z, superseded);
}
}
long lo = 0, hi = H, lo2 = 0, hi2 = 0;
const unsigned char* src = nullptr;
long srcY = 0;
@@ -4094,8 +4125,11 @@ bool BrowserPageGoanna::xStart(int64_t now, double Z, const XWant& want, int64_t
int xWin = xWinOn ? (int)((xCap - H) / 2) : 0;
bool own = false, xMoved = false;
long ownDy = 0;
if (!aBase && xWinOn && !sNoOwn && mGpuSlotValid[slot] && mGpuSlotX[slot] == x && mGpuSlotZoom[slot] == Z &&
mGpuSlotW[slot] == w && mGpuSlotH[slot] == H && mGpuFixedHi >= mGpuFixedLo) {
// (review) only when every composite is in the slots' records (no main-lane job composed and not
// yet polled), and with no fixed or stuck layers then or in the slot's frame (the engine judges
// those against the previous composite, not an older base)
if (!aBase && xWinOn && !sNoOwn && mGpuJobs.empty() && mGpuSlotValid[slot] && mGpuSlotX[slot] == x && mGpuSlotZoom[slot] == Z &&
mGpuSlotW[slot] == w && mGpuSlotH[slot] == H && mGpuFixedHi <= mGpuFixedLo && !mGpuSlotFixed[slot]) {
const long d = (long)y - mGpuSlotY[slot];
const long stale = (mGpuStaleHi[slot] - mGpuStaleLo[slot]) + (mGpuStale2Hi[slot] - mGpuStale2Lo[slot]);
// the faster kind by measurement, when there is a copy to compare with (a base)
@@ -4115,6 +4149,7 @@ bool BrowserPageGoanna::xStart(int64_t now, double Z, const XWant& want, int64_t
if (n < 0 || n + H > xCap) { // near an end: the frame back to the middle first, one copy
const long c = (xCap - H) / 2, to = c - d;
if (to >= 0 && to + H <= xCap) {
mGpuSlotValid[slot] = false; // (review) its rows move: no record of them holds until the publish
memmove(S.base[slot] + S.hdr + (size_t)to * xStride, S.base[slot] + S.hdr + (size_t)mGpuSlotWin[slot] * xStride,
(size_t)H * xStride);
n = c;
@@ -4194,6 +4229,7 @@ bool BrowserPageGoanna::xStart(int64_t now, double Z, const XWant& want, int64_t
return false;
}
mXLastPostToken = token;
mGpuSlotValid[slot] = false; // T-517 (review): being written; its publish records it again
XJob& j = aBase ? mXJob2 : mXJob;
j = XJob();
j.on = true; j.token = token; j.gen = mXGeomGen; j.slot = slot; j.key = S.keys[slot];
@@ -4278,6 +4314,7 @@ bool BrowserPageGoanna::xPoll(int64_t now) {
mGpuStaleLo[s] = mGpuStaleHi[s] = mGpuStale2Lo[s] = mGpuStale2Hi[s] = 0;
mGpuStaleX0[s] = mGpuStaleX1[s] = 0;
mGpuSlotWin[s] = mXJob.win; // T-517
mGpuSlotFixed[s] = cr.fixedHi > cr.fixedLo; // (review)
mGpuLatestSlot = s; mGpuLatestKey = mXJob.key;
mGpuFixedLo = cr.fixedLo; mGpuFixedHi = cr.fixedHi;
mBufFull[s].valid = false;
@@ -5096,6 +5133,7 @@ void BrowserPageGoanna::paintToSharedBuffer() {
}
// a compositor that takes jobs and composes nothing (no GL: the engine fell back to its
// software compositor, round 229) fails every one; three in a row without a good one between
if (r == -1) gpuSlotsLost(); // T-517 (review): it may have been composed: its change is in no record
if (r == -1 && ++mGpuRefusals >= 3) { gpuFallBack("the compositor failed three frames"); return; }
if (r >= 0) mGpuRefusals = 0;
if (r == -2 || r == -3) {
@@ -5207,6 +5245,7 @@ void BrowserPageGoanna::paintToSharedBuffer() {
gpuFinished = true;
pubWin = oldest.win; // T-517
mGpuSlotWin[gslot] = oldest.win;
mGpuSlotFixed[gslot] = cr.fixedHi > cr.fixedLo; // (review)
gpuT[0] = oldest.t0;
mGpuJobX = oldest.x; mGpuJobY = frameY; mGpuJobZoom = oldest.zoom;
mGpuJobViewRow = oldest.viewRow; // T-507: where the card's view sits in this frame
+3
View File
@@ -452,6 +452,9 @@ private:
// T-517: the row of each slot's pixels its GPU frame starts at (the frame window,
// JihadFrameWindow.h); 0 when windows are off, and for the software path's frames
int mGpuSlotWin[3] = {0, 0, 0};
// T-517 (review): the slot's frame had fixed or stuck layers (the engine judges kept rects and
// fixed columns against the previous composite, so an older base is only taken without them)
bool mGpuSlotFixed[3] = {false, false, false};
bool windowsFor(int key); // the adapter that made the segment reads windows, and they are on
// a composite's change into the other valid slots' stale rows (both lanes, T-517), and the
// records dropped when a composite's change is in none of them
+2
View File
@@ -73,6 +73,8 @@ extern "C" {
int jihad_offscreen_paint_layers(nsIWidget* aWidget, nsIDocShell* aDocShell) __attribute__((weak));
// peek: a job's state without taking its result (1 done, 2 composed and being read, 0 pending, -1 unknown)
int jihad_offscreen_compose_peek(nsIWidget* aWidget, uint32_t aToken) __attribute__((weak));
// OPTIONAL (T-511 review): the daemon gave the GPU path up; WebGL makes no more EGL contexts
void jihad_offscreen_gpu_given_up() __attribute__((weak));
uint32_t jihad_offscreen_compose_post(nsIWidget* aWidget, void* aDest, int aStride, int aWidth, int aHeight,
int aRowLo, int aRowHi, int aRow2Lo, int aRow2Hi, int aShift,
const void* aSrc, int aSrcY, int aFlags, int aUseView, double aScale,
+1 -1