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:
1 parent
20ccf6b553
commit
902187aebe
11 files changed
+264
-10
No files matched your search
+141
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
@@ -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 |
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Vendored
+1
-1
Submodule third_party/uxp updated: a563423cac...66180dfea4.
Reference in new issue
Block a user