Files
Jihad-Browser/build/desktop/patches/0071-gpu-compositor-the-parts-picture-in-the-frame-s-own-.patch
T
NotAlexNoyleandClaude Fable 5.1 62af6e82c7 The engine on UXP RB_20261027 (T-551..T-554): the queue rebased, 0060 dropped, every pin, the device checked
The owner's choice of 2026-11-02 after T-551's check found the tag: the update now, patch 0060 (the
roots, byte-identical upstream) dropped, special attention to the certificate and HTTPS changes.
- update-engine.sh RB_20261027 / --finish: the 72 patches on the tag (0060 fell away as already
  applied), jihad/uxp-RB_20261027 at 52ad9ab66e in third_party/uxp, the queue rewritten and
  renumbered from 0060, the pins (engine-version.env, the OE recipe, .gitmodules, the UA and the
  three start pages: UXP/6b18be9), JIHAD_APP_BUILD_ID 20261103000000; patch-queue.sh --check,
  check-engine-pins.sh and check-engine-api.py (baseline rewritten, unchanged) agree.
- docs/ENGINE-SOURCE.md: the base, the dropped 0060, the renumbering, and the range's 12 commits
  each with its disposition — the two NSS certificate commits (emSign removed after the eMudhra
  compromise; certdata to Sept 2026) were on the device since the roots of 2026-10-25 and are the
  base's now; no PSM, TLS protocol, HSTS or certificate-UI change in the range; the memory-safety
  fixes (the JS OOB array copy, the jar: flag, feTile, the layout frame table, the mutation
  observer's strong ref, SQLite 3.54.1) taken with the base.
- audit-roots.py against the fetched release certdata and the CCADB report: identical set and
  trust, none removed, RESULT OK (docs/rounds/certs-2026-11-03).
- Desktop: the full build clean; 14 of 18 tests pass, the four left are the desktop GTK path's
  known read-back limit. ARM: libxul, the daemon, the bundle, the three packages; full pushes to
  all three variants; default-check x3 faults 0; the daemon's start line 28 of 29 optional
  exports as before. The areas the commits touch, on the device (tools/device/engine-checks.sh,
  new): about:settings, about:personas and about:addons through jar: under the flag change,
  cookies through the new SQLite, an SVG filter page with feTile and its overflow case
  (render/goanna/test/jihad-svg-filter.html, new), a long press's selection; faults 0.
  The certificate kit's checks (tools/device/cert-checks.sh): the TLS 1.0 refusal, the clean loads,
  expired / wrong.host / self-signed / untrusted-root with the SSL confirm's right flags, OCSP
  revocation enforced on grc.com, ssl.com and DigiCert's demos, revoked.badssl.com's CRL-only case
  invisible as before: nothing changed with the base.
  Records: docs/rounds/engine-rb27-2026-11-03/notes.txt.
- tools/device/cert-checks.sh and t502/enyo-restore-job.sh (new): the certificate kit's device
  checks under a hand-started daemon, and the Enyo job put back after any spike round.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-03 10:25:46 -05:00

598 lines
36 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NotAlexNoyle <alexnoyle@icloud.com>
Date: Mon, 26 Oct 2026 14:42:39 -0400
Subject: [PATCH] gpu-compositor: the parts' picture in the frame's own job,
from the same tree state (E2)
T-505, after two adversarial reviews of the parts on the CPU (the inspector's design E2): a frame
composed without its parts (flag 128) and posted with compose_post2/start2 (overview_ok answers 3)
carries a parts buffer and the signature of the picture the daemon holds.
- In the job, before the frame's compose: the parts' list (every fixed and sticky layer, whole
rects), their signature (each part subtree's layers: identity, type, opacity, content flags,
mask, visible region; inside a part its transform and clip; a colour layer's colour; an
animation every composite; the paints under a part; the parts' sizes), the card's hold set
(fixed and stuck layers, T-544's meaning) and a cover flag (a content leaf drawn above a part
whose pixels meet it). When the signature is not the daemon's, the parts alone are composed on
the band surface, packed in shelves, and read with their alpha into the buffer (a verify guard
holds the diff baseline, the probe, the last scroll and view). Flag 2: the parts too big.
- The poll's 49 ints: the signature, the pass's bytes, the flags, the hold set.
- Review fixes: no column reads in a frame without its parts (the daemon restores what lay beneath
them by whole rows); 0071's own-slot compensation only without the parts or with no fixed rects.
- LayerManagerComposite::JihadPrepareOnly (visible regions and effective transforms without the
diff), CompositorOGL::JihadReadPacked (a packed read that keeps the alpha).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XtfxSpM1w2uiVKeLKoV3g3
---
.../composite/LayerManagerComposite.cpp | 12 ++
gfx/layers/composite/LayerManagerComposite.h | 3 +
gfx/layers/ipc/CompositorBridgeParent.cpp | 204 +++++++++++++++++-
gfx/layers/ipc/CompositorBridgeParent.h | 3 +
gfx/layers/opengl/CompositorOGL.cpp | 33 +++
gfx/layers/opengl/CompositorOGL.h | 17 +-
widget/PuppetWidget.cpp | 60 +++++-
widget/PuppetWidget.h | 9 +-
8 files changed, 323 insertions(+), 18 deletions(-)
diff --git a/gfx/layers/composite/LayerManagerComposite.cpp b/gfx/layers/composite/LayerManagerComposite.cpp
index 124b20c3fa..280b884634 100644
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -417,6 +417,18 @@ LayerManagerComposite::EndTransaction(const TimeStamp& aTimeStamp,
#endif
}
+void
+LayerManagerComposite::JihadPrepareOnly()
+{
+ if (!mRoot) {
+ return;
+ }
+ nsIntRegion opaque;
+ LayerIntRegion visible;
+ PostProcessLayers(mRoot, opaque, visible, Nothing());
+ mRoot->ComputeEffectiveTransforms(getenv("JIHAD_GPU_NO_WORLD_ENGINE") ? gfx::Matrix4x4() : mJihadWorld);
+}
+
nsIntRegion
LayerManagerComposite::JihadDiffOnly()
{
diff --git a/gfx/layers/composite/LayerManagerComposite.h b/gfx/layers/composite/LayerManagerComposite.h
index e8f22dc1ab..c49ecb73a6 100644
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -146,6 +146,9 @@ public:
// round 119: the tree diff now, without a render (the stored tree refreshed as a composite
// would), in frame pixels; the caller has begun a transaction and applied the async transforms
nsIntRegion JihadDiffOnly();
+ // T-505: the visible regions and effective transforms as a composite would make them, without the diff (the
+ // stored tree untouched); the caller has begun a transaction and applied the async transforms
+ void JihadPrepareOnly();
void JihadEndTransactionNoComposite() { mInTransaction = false; }
bool JihadPartial() const { return mJihadPartialOn; }
diff --git a/gfx/layers/ipc/CompositorBridgeParent.cpp b/gfx/layers/ipc/CompositorBridgeParent.cpp
index 177fc4c51d..420a0c7cbf 100644
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -1167,6 +1167,80 @@ CompositorBridgeParent::JihadHideForJob()
});
}
+// T-505 (E2): FNV-1a over what makes the parts' pixels: each layer in a fixed or sticky subtree (its identity and
+// type, opacity, content flags, mask, visible region; inside a part its transform and clip too, so an inner scroll or a
+// move inside the part counts; a colour layer's colour; an animation: every composite), the paints of layers under a
+// part (gJihadPartsGen) and the parts' sizes on the frame (a part crossing the frame's edge is new pixels). A content
+// leaf drawn after a part (above it, in paint order) whose pixels meet it is a cover: a popup over a fixed bar, a page
+// over a fixed background
+uint32_t
+CompositorBridgeParent::JihadPartsSig(const gfx::IntRect* aRects, int aN, bool* aCovered)
+{
+ uint32_t h = 2166136261u;
+ auto mix = [&](uint32_t v) { h = (h ^ v) * 16777619u; };
+ auto mixf = [&](float f) { uint32_t u; memcpy(&u, &f, sizeof u); mix(u); };
+ auto mixr = [&](const gfx::IntRect& r) { mix(r.x); mix(r.y); mix(r.width); mix(r.height); };
+ mix((uint32_t)gJihadPartsGen);
+ for (int i = 0; i < aN; i++) { mix(aRects[i].width); mix(aRects[i].height); }
+ *aCovered = false;
+ Layer* root = mLayerManager ? mLayerManager->GetRoot() : nullptr;
+ if (!root) return h;
+ static uint32_t sAnimTick = 0;
+ static uint32_t sCoverLog = 0;
+ int depth = 0;
+ nsIntRegion partsSoFar;
+ auto onFrame = [&](Layer* x, nsIntRegion& aOut) {
+ gfx::Matrix m;
+ if (!x->GetEffectiveTransform().Is2D(&m) || !m.PreservesAxisAlignedRectangles()) {
+ const gfx::IntRect vb = x->GetLocalVisibleRegion().GetBounds().ToUnknownRect();
+ aOut.OrWith(gfx::RoundedOut(x->GetEffectiveTransform().TransformBounds(gfx::Rect(vb.x, vb.y, vb.width, vb.height))));
+ return;
+ }
+ const nsIntRegion visible = x->GetLocalVisibleRegion().ToUnknownRegion();
+ for (auto it = visible.RectIter(); !it.Done(); it.Next()) {
+ const gfx::IntRect& q = it.Get();
+ aOut.OrWith(gfx::RoundedOut(x->GetEffectiveTransform().TransformBounds(gfx::Rect(q.x, q.y, q.width, q.height))));
+ }
+ };
+ ForEachNode<ForwardIterator>(root,
+ [&](Layer* l) {
+ const bool part = l->GetIsFixedPosition() || l->GetIsStickyPosition();
+ if (part || depth > 0) {
+ mix((uint32_t)(uintptr_t)l); mix((uint32_t)l->GetType()); mixf(l->GetOpacity());
+ mix(l->GetContentFlags()); mix(l->GetMaskLayer() ? 1u : 0u);
+ mixr(l->GetVisibleRegion().GetBounds().ToUnknownRect());
+ if (depth > 0) {
+ const gfx::Matrix4x4 m = l->GetBaseTransform();
+ for (int k = 0; k < 16; k++) mixf(m.components[k]);
+ if (l->GetClipRect()) mixr(l->GetClipRect()->ToUnknownRect());
+ }
+ if (l->GetType() == Layer::TYPE_COLOR) {
+ const gfx::Color c = static_cast<ColorLayer*>(l)->GetColor();
+ mixf(c.r); mixf(c.g); mixf(c.b); mixf(c.a);
+ }
+ if (!l->GetAnimations().IsEmpty()) mix(++sAnimTick);
+ if (part && depth == 0) onFrame(l, partsSoFar);
+ } else if (!l->GetFirstChild() && !partsSoFar.IsEmpty() && l->GetEffectiveOpacity() > 0.0f) {
+ const Layer::LayerType t = l->GetType();
+ if (t == Layer::TYPE_PAINTED || t == Layer::TYPE_COLOR || t == Layer::TYPE_IMAGE || t == Layer::TYPE_CANVAS) {
+ nsIntRegion mine;
+ onFrame(l, mine);
+ mine.AndWith(partsSoFar);
+ if (!mine.IsEmpty()) {
+ if (!*aCovered && sCoverLog++ < 20) {
+ const gfx::IntRect b = mine.GetBounds();
+ JIHAD_GPU_LOG("[jihad-gpu] parts: layer %p (type %d) drawn above a part at %d,%d %dx%d\n", l, (int)t, b.x, b.y, b.width, b.height);
+ }
+ *aCovered = true;
+ }
+ }
+ }
+ if (part) depth++;
+ },
+ [&](Layer* l) { if (l->GetIsFixedPosition() || l->GetIsStickyPosition()) depth--; });
+ return h;
+}
+
void
CompositorBridgeParent::CompositeToTarget(DrawTarget* aTarget, const gfx::IntRect* aRect)
{
@@ -1880,8 +1954,10 @@ CompositorBridgeParent::JihadComposeAndRead(JihadFrameRequest* aReq, float aScal
// transaction during a pan was the whole frame changed for it (the content's translation), and a whole read
// with the other two slots' frames made stale whole. Only the card's scroll: the shift the diff measured must
// be the card's since the last composite (a scroll the page made itself is then the whole frame changed)
+ // (review: not for a frame with the engine's parts while fixed or stuck rects exist: its keeps and columns are
+ // judged against the last composite, not the older base)
const bool ownComp = aReq->src == nullptr && aReq->scrollValid && mJihadLastScrollValid &&
- aReq->scrollX == mJihadLastScrollX;
+ aReq->scrollX == mJihadLastScrollX && (sJihadContentOnly || mJihadLastFixedN == 0);
const float ownExpectRows = (aReq->scrollY - mJihadLastScrollY) * (aScale > 0.0f ? aScale : 1.0f);
auto ownShiftOk = [&]() {
float sdx = 0.0f, sdy = 0.0f;
@@ -1907,6 +1983,119 @@ CompositorBridgeParent::JihadComposeAndRead(JihadFrameRequest* aReq, float aScal
const bool treeChanged = !mJihadSeqKnown || seqNow != mJihadSeqAtLast;
mJihadSeqAtLast = seqNow; mJihadSeqKnown = true;
const bool scrollOnly = scrollChanged && !viewChanged && !treeChanged;
+ // T-505 (E2): a frame without its parts: their list, signature and the card's hold set from this job's tree state,
+ // and their picture read here when the daemon holds none of this signature (CompositorOGL.h, partsDest)
+ bool jPartsPrepped = false;
+ if (aReq->contentOnly && !aReq->overviewParts && !aReq->verify && mCompositionManager && mLayerManager->GetRoot()) {
+ jPartsPrepped = true;
+ gfx::IntRect parts[4], pk[4];
+ mLayerManager->BeginTransaction();
+ SetShadowProperties(mLayerManager->GetRoot());
+ mCompositionManager->ComputeRotation();
+ {
+ TimeStamp time = mIsTesting ? mTestTime : mCompositorScheduler->GetLastComposeTime();
+ mCompositionManager->TransformShadowTree(time, mVsyncRate);
+ }
+ mLayerManager->JihadPrepareOnly();
+ const int partN = JihadFixedRectsNow(aWidth, aHeight, parts, pk); // every fixed and sticky layer, whole rects
+ bool covered = false;
+ aReq->partsGen = JihadPartsSig(parts, partN, &covered);
+ if (covered) aReq->partsFlags |= 1;
+ {
+ sJihadContentOnly = false; // the card's hold set keeps T-544's meaning: fixed layers and stuck ones
+ gfx::IntRect hr[4], hk[4];
+ const int hn = JihadFixedRectsNow(aWidth, aHeight, hr, hk);
+ sJihadContentOnly = true;
+ aReq->nHold = 0;
+ for (int k = 0; k < hn && aReq->nHold < 4; k++) {
+ if (hr[k].IsEmpty()) continue;
+ int* o = aReq->hold[aReq->nHold++];
+ o[0] = hr[k].x; o[1] = hr[k].y; o[2] = hr[k].XMost(); o[3] = hr[k].YMost();
+ }
+ }
+ mLayerManager->JihadEndTransactionNoComposite();
+ JihadStuckOut(aReq, parts, partN);
+ if (!covered && aReq->partsDest && aReq->nStuck > 0 && aReq->partsGen != aReq->partsHave) {
+ size_t need = 0;
+ for (int k = 0; k < aReq->nStuck; k++)
+ need += (size_t)(aReq->stuck[k][2] - aReq->stuck[k][0]) * (aReq->stuck[k][3] - aReq->stuck[k][1]) * 4;
+ // the rects cut into pieces no taller than the surface, on shelves across its width (12 regions at most)
+ struct Piece { int k, x, y, w, h, atX, atY; } pieces[12];
+ int np = 0, used = 0;
+ auto pack = [&](int aCap) {
+ np = 0; used = 0;
+ int shelfY = 0, shelfH = 0, cursorX = 0;
+ for (int k = 0; k < aReq->nStuck; k++) {
+ const int x0 = aReq->stuck[k][0], w = aReq->stuck[k][2] - x0;
+ for (int y = aReq->stuck[k][1]; y < aReq->stuck[k][3];) {
+ const int hgt = std::min(aCap, aReq->stuck[k][3] - y);
+ if (cursorX + w > aWidth) { shelfY += shelfH; shelfH = 0; cursorX = 0; }
+ if (w > aWidth || shelfY + hgt > aCap || np == 12) return false;
+ pieces[np++] = { k, x0, y, w, hgt, cursorX, shelfY };
+ cursorX += w; shelfH = std::max(shelfH, hgt); y += hgt;
+ }
+ }
+ used = shelfY + shelfH;
+ return true;
+ };
+ const bool fits = need <= (size_t)aReq->partsCap &&
+ (pack(ogl->JihadBandRows()) || (ogl->JihadBandTallRows() > 0 && pack(ogl->JihadBandTallRows())));
+ if (!fits) {
+ aReq->partsFlags |= 2;
+ } else {
+ JihadVerifyGuard partsGuard(this, true); // the baseline, the probe, the last scroll and view held
+ const int passRows = ogl->JihadBeginBandPass(aWidth, used);
+ if (!passRows) {
+ aReq->partsFlags |= 2;
+ } else {
+ sJihadOverview = 2; // the parts alone (JihadHideForJob)
+ ogl->JihadSetClearTransparent(true);
+ gfx::Matrix4x4 worlds[12];
+ gfx::IntRect rects[12];
+ for (int i = 0; i < np; i++) {
+ worlds[i] = world;
+ worlds[i].PostTranslate((float)(pieces[i].atX - pieces[i].x), (float)(pieces[i].atY - pieces[i].y), 0.0f);
+ rects[i] = gfx::IntRect(pieces[i].atX, pieces[i].atY, pieces[i].w, pieces[i].h);
+ }
+ mLayerManager->JihadSetWorldTransform(worlds[0]);
+ mLayerManager->UpdateRenderBounds(gfx::IntRect(0, 0, aWidth, passRows));
+ mLayerManager->JihadIgnoreDiff(true);
+ mLayerManager->JihadResetLastInvalid();
+ mLayerManager->JihadSetBand(gfx::IntRect(), gfx::IntRect());
+ mLayerManager->JihadSetRegions(worlds, rects, np);
+ const TimeStamp pc0 = TimeStamp::Now();
+ sJihadInCompose = true;
+ ForceComposeToTarget(nullptr, nullptr);
+ sJihadInCompose = false;
+ mLayerManager->JihadSetRegions(nullptr, nullptr, 0);
+ const TimeStamp pr0 = TimeStamp::Now();
+ bool ok = true;
+ size_t off[4] = {};
+ for (int k = 1; k < aReq->nStuck; k++)
+ off[k] = off[k - 1] + (size_t)(aReq->stuck[k - 1][2] - aReq->stuck[k - 1][0]) * (aReq->stuck[k - 1][3] - aReq->stuck[k - 1][1]) * 4;
+ for (int i = 0; i < np && ok; i++) {
+ const int k = pieces[i].k, rw = aReq->stuck[k][2] - aReq->stuck[k][0];
+ uint8_t* d = aReq->partsDest + off[k] + (size_t)(pieces[i].y - aReq->stuck[k][1]) * rw * 4;
+ ok = ogl->JihadReadPacked(d, rw * 4, passRows, pieces[i].atX, pieces[i].w, pieces[i].atY, pieces[i].h);
+ }
+ ogl->JihadEndBandPass();
+ ogl->JihadSetClearTransparent(false);
+ sJihadOverview = 0;
+ if (ok) aReq->partsBytes = (int)need;
+ static uint32_t sPassLog = 0;
+ if (sPassLog++ < 20 || (sPassLog % 50) == 0)
+ JIHAD_GPU_LOG("[jihad-gpu] parts pass: %d parts, %d pieces on %d rows, %zu KB: compose %.1f ms, read %.1f ms%s\n",
+ aReq->nStuck, np, used, need / 1024, (pr0 - pc0).ToMilliseconds(), (TimeStamp::Now() - pr0).ToMilliseconds(),
+ ok ? "" : " (failed)");
+ }
+ mLayerManager->JihadIgnoreDiff(false);
+ mLayerManager->JihadSetWorldTransform(world);
+ mLayerManager->UpdateRenderBounds(whole);
+ mLayerManager->JihadSetBand(gfx::IntRect(), gfx::IntRect());
+ ogl->JihadSelectTarget();
+ }
+ }
+ }
// Round 119: a frame whose tree changed takes the diff first, without a render. When what it
// changed, the bands the daemon asked for, the entering band of a scroll the page made and the
// fixed rows fit the band pass, the frame is composed there like a scroll-only one (the band
@@ -2190,7 +2379,8 @@ CompositorBridgeParent::JihadComposeAndRead(JihadFrameRequest* aReq, float aScal
ogl->JihadSetCancel(&aReq->cancelled);
// T-511: one band and no column chunks: the band read as the column the change spans
int cx0 = 0, cx1 = 0;
- const bool colRead = ok && diffFirst && hs[0] > 0 && !hs[1] && nChunks == 0 &&
+ // (T-505 review: not without the parts: the daemon puts back what lay beneath them by whole rows)
+ const bool colRead = ok && diffFirst && !sJihadContentOnly && hs[0] > 0 && !hs[1] && nChunks == 0 &&
JihadColumnRead(aReq, dfInvX0, dfInvX1, dfInvLo, dfInvHi, aWidth, &cx0, &cx1);
if (colRead) {
TimeStamp r0 = TimeStamp::Now();
@@ -2256,8 +2446,8 @@ CompositorBridgeParent::JihadComposeAndRead(JihadFrameRequest* aReq, float aScal
});
if (fHi > fLo) { mJihadLastFixedLo = fLo; mJihadLastFixedHi = fHi; } else { mJihadLastFixedLo = mJihadLastFixedHi = 0; }
if (sJihadContentOnly) {
- gfx::IntRect pr[4], pk[4]; // T-505: where the parts are, for the daemon to draw them
- JihadStuckOut(aReq, pr, JihadFixedRectsNow(aWidth, aHeight, pr, pk));
+ gfx::IntRect pr[4], pk[4]; // T-505: where the parts are, for the daemon to draw them (E2: the prep's)
+ if (!jPartsPrepped) JihadStuckOut(aReq, pr, JihadFixedRectsNow(aWidth, aHeight, pr, pk));
} else {
JihadUpdateLastFixed(aWidth, aHeight); // rounds 138/141
JihadStuckOut(aReq, mJihadLastFixedRects, mJihadLastFixedN);
@@ -2390,8 +2580,8 @@ CompositorBridgeParent::JihadComposeAndRead(JihadFrameRequest* aReq, float aScal
gfx::IntRect colRects[8], colKeeps[8];
int colRectsN = 0;
if (sJihadContentOnly) {
- gfx::IntRect pr[4], pk[4]; // T-505: where the parts are, for the daemon to draw them
- JihadStuckOut(aReq, pr, JihadFixedRectsNow(aWidth, aHeight, pr, pk));
+ gfx::IntRect pr[4], pk[4]; // T-505: where the parts are, for the daemon to draw them (E2: the prep's)
+ if (!jPartsPrepped) JihadStuckOut(aReq, pr, JihadFixedRectsNow(aWidth, aHeight, pr, pk));
}
if (JihadFixedCols()) {
gfx::IntRect lastR[4];
@@ -2473,7 +2663,7 @@ CompositorBridgeParent::JihadComposeAndRead(JihadFrameRequest* aReq, float aScal
aReq->invX0 = invX0; aReq->invX1 = invX1;
{
int cx0 = 0, cx1 = 0; // T-511: the bands read as one column (JihadReadAsync)
- if (!aReq->bandsOnly && JihadColumnRead(aReq, invX0, invX1, invLo, invHi, aWidth, &cx0, &cx1)) { aReq->readX0 = cx0; aReq->readX1 = cx1; }
+ if (!aReq->bandsOnly && !sJihadContentOnly && JihadColumnRead(aReq, invX0, invX1, invLo, invHi, aWidth, &cx0, &cx1)) { aReq->readX0 = cx0; aReq->readX1 = cx1; }
}
// the log's values now: once the read hands the frame over, aReq belongs to the daemon
const int logLo2 = aReq->lo2, logHi2 = aReq->hi2, logFixedLo = aReq->fixedLo, logFixedHi = aReq->fixedHi, logCols = aReq->nCols, logKeep = aReq->nKeep;
diff --git a/gfx/layers/ipc/CompositorBridgeParent.h b/gfx/layers/ipc/CompositorBridgeParent.h
index 7b021a883d..5776ec30c4 100644
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -546,6 +546,9 @@ public:
gfx::IntRect mJihadLastKeep[4], mJihadPrevKeep[4];
int mJihadPrevKeepN = 0;
void JihadHideForJob(); // Jihad T-504/T-505: the overview's and a content-only frame's layers hidden
+ // Jihad T-505 (E2): the parts' signature (their layers, content, attributes and sizes); *aCovered when a content
+ // layer is drawn above a part (the daemon cannot draw that part over the frame)
+ uint32_t JihadPartsSig(const gfx::IntRect* aRects, int aN, bool* aCovered);
int JihadFixedRectsNow(int aWidth, int aHeight, gfx::IntRect* aOut, gfx::IntRect* aKeep);
void JihadUpdateLastFixed(int aWidth, int aHeight);
bool mJihadSeqKnown = false;
diff --git a/gfx/layers/opengl/CompositorOGL.cpp b/gfx/layers/opengl/CompositorOGL.cpp
index 5f3bc77030..d16d74dcfc 100644
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -2759,6 +2759,39 @@ CompositorOGL::JihadReadFrame(uint8_t* aDest, int aStride, int aWidth, int aHeig
return true;
}
+bool
+CompositorOGL::JihadReadPacked(uint8_t* aDest, int aDestStride, int aSurfaceH, int aX0, int aW, int aSrcRowLo, int aRows)
+{
+ if (!mGLContext || !aDest || aW <= 0 || aRows <= 0 || aSrcRowLo < 0 || aSrcRowLo + aRows > aSurfaceH || aX0 < 0 ||
+ aDestStride < aW * 4) {
+ return false;
+ }
+ static uint8_t* sBuf = nullptr;
+ static size_t sCap = 0;
+ const size_t bytes = (size_t)aW * 4 * aRows;
+ if (sCap < bytes) {
+ free(sBuf);
+ sBuf = (uint8_t*)malloc(bytes);
+ sCap = sBuf ? bytes : 0;
+ if (!sBuf) return false;
+ }
+ mGLContext->MakeCurrent();
+ mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mJihadWindowFBO);
+ {
+ ScopedPackState safePackState(mGLContext);
+ mGLContext->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 4);
+ mGLContext->fReadPixels(aX0, aSurfaceH - (aSrcRowLo + aRows), aW, aRows, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, sBuf);
+ }
+ if (mJihadCancel && *mJihadCancel) {
+ return false;
+ }
+ JihadSwizzleAndFlipRows(sBuf, aW * 4, aW, aRows, false); // the parts keep their alpha
+ for (int r = 0; r < aRows; r++) {
+ memcpy(aDest + (size_t)r * aDestStride, sBuf + (size_t)r * aW * 4, (size_t)aW * 4);
+ }
+ return true;
+}
+
bool
CompositorOGL::JihadReadColumns(uint8_t* aDest, int aStride, int aSurfaceH, int aX0, int aX1, int aSrcRowLo, int aRows, int aDestRowLo,
int aDestX)
diff --git a/gfx/layers/opengl/CompositorOGL.h b/gfx/layers/opengl/CompositorOGL.h
index 12ef1ecb53..f33ebe4908 100644
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -176,7 +176,19 @@ struct JihadFrameRequest {
// layer at opacity 0 (stuck or not), no fixed rows, keeps or columns; the
// parts' rects still reported (the daemon draws them on the CPU). With flag
// 64, the parts alone with the same rule
- uint32_t partsGen = 0; // T-505, out: the count of updates to layers under a fixed or sticky one
+ uint32_t partsGen = 0; // T-505, out: the parts' signature (the poll's thirtieth int; see partsHave)
+ // T-505 (E2, the inspector's design): a frame without its parts reads the parts' picture in its own job when their
+ // signature (their layers' content, attributes, structure and sizes) is not the one the daemon holds a picture of:
+ // the parts alone, cleared transparent, packed on the band surface and read with their alpha into partsDest (each
+ // rect of stuck[] in turn, its rows one after the other, B8G8R8A8 premultiplied), from the same tree state as the
+ // frame. stuck[] is then the parts' list (every fixed and sticky layer), hold[] the card's (fixed and stuck ones)
+ uint8_t* partsDest = nullptr;
+ int partsCap = 0;
+ uint32_t partsHave = 0;
+ int partsBytes = 0; // out: the bytes the job's parts pass wrote (0: no pass)
+ int partsFlags = 0; // out: 1 a content layer above a part (the frame cannot be dressed), 2 too big
+ int hold[4][4] = {}; // out: the card's hold set (x0, y0, x1, y1) of a frame without its parts
+ int nHold = 0;
int fixedLo = 0, fixedHi = 0; // out: the rows fixed and sticky layers cover (0,0 = none)
int cols[4][4] = {}; // round 138: column reads after the bands (x0, x1, y0, y1 on the
int nCols = 0; // frame): the fixed and stuck layers' rects, widened by the shift
@@ -242,6 +254,9 @@ public:
// target aSurfaceH rows tall) into the frame's rows aDestRowLo.. at the same columns
bool JihadReadColumns(uint8_t* aDest, int aStride, int aSurfaceH, int aX0, int aX1, int aSrcRowLo, int aRows, int aDestRowLo,
int aDestX = -1); // round 142: the frame's column when packed elsewhere on the target
+ // T-505: aW columns from aX0 of the target's rows aSrcRowLo.. (aRows, top-down, on a target aSurfaceH rows tall)
+ // into aDest (aDestStride a row), the alpha kept
+ bool JihadReadPacked(uint8_t* aDest, int aDestStride, int aSurfaceH, int aX0, int aW, int aSrcRowLo, int aRows);
// The asynchronous frame: SelectTarget before the compose picks the free window target,
// ReadAsync after it fences the compose and hands the target to the read thread, which reads
// rows [lo, hi) into dest and calls done (on the read thread). When no read thread could be
diff --git a/widget/PuppetWidget.cpp b/widget/PuppetWidget.cpp
index 6535e3e649..5a45a2f1f2 100644
--- a/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -852,6 +852,7 @@ struct JihadReadJob {
bool overview = false; // T-504 (flag 32): a window of the daemon's overview
bool overviewParts = false; // T-504 (flag 64): the overview's fixed and stuck parts alone
bool contentOnly = false; // T-505 (flag 128): the frame without its fixed and sticky parts
+ uint8_t* partsDest = nullptr; int partsCap = 0; uint32_t partsHave = 0; // T-505 (E2): its parts pass
int invLo, invHi; // the rows the composite changed
float scrollX = 0.0f, scrollY = 0.0f; bool scrollValid = false; // the card's scroll for the compositor
uint32_t token;
@@ -903,6 +904,7 @@ void JihadReadOnCompositorThread(JihadReadJob* job)
job->req.overview = job->overview;
job->req.overviewParts = job->overviewParts;
job->req.contentOnly = job->contentOnly;
+ job->req.partsDest = job->partsDest; job->req.partsCap = job->partsCap; job->req.partsHave = job->partsHave;
job->req.scrollX = job->scrollX; job->req.scrollY = job->scrollY; job->req.scrollValid = job->scrollValid;
job->req.done = &JihadJobDone; job->req.cookie = job;
// Once done() has run the daemon may forget (delete) the job at any moment (round 108's wake
@@ -938,7 +940,8 @@ PuppetWidget::JihadBridge()
uint32_t
PuppetWidget::JihadComposeStart(nsIDocShell* aDocShell, 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 aColX0, int aColX1)
+ const void* aSrc, int aSrcY, int aFlags, int aColX0, int aColX1,
+ void* aPartsDest, int aPartsCap, uint32_t aPartsHave)
{
if (!aDocShell || !aDest || aWidth <= 0 || aHeight <= 0 || aStride < aWidth * 4) {
return 0;
@@ -947,7 +950,7 @@ PuppetWidget::JihadComposeStart(nsIDocShell* aDocShell, void* aDest, int aStride
return 0;
}
return JihadPostJob(aDest, aStride, aWidth, aHeight, aRowLo, aRowHi, aRow2Lo, aRow2Hi, aShift, aSrc, aSrcY, aFlags,
- nullptr, false, aColX0, aColX1);
+ nullptr, false, aColX0, aColX1, aPartsDest, aPartsCap, aPartsHave);
}
bool
@@ -1038,7 +1041,8 @@ PuppetWidget::JihadPaintLayers(nsIDocShell* aDocShell)
uint32_t
PuppetWidget::JihadPostJob(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,
- const float* aView, bool aScrollValid, int aColX0, int aColX1)
+ const float* aView, bool aScrollValid, int aColX0, int aColX1,
+ void* aPartsDest, int aPartsCap, uint32_t aPartsHave)
{
mozilla::layers::CompositorBridgeParent* bridge = JihadBridge();
MessageLoop* loop = mozilla::layers::CompositorThreadHolder::Loop();
@@ -1063,6 +1067,9 @@ PuppetWidget::JihadPostJob(void* aDest, int aStride, int aWidth, int aHeight, in
job->overview = (aFlags & 32) != 0; // T-504
job->overviewParts = (aFlags & 64) != 0;
job->contentOnly = (aFlags & 128) != 0; // T-505
+ if (job->contentOnly && aPartsDest && aPartsCap > 0) { // T-505 (E2)
+ job->partsDest = static_cast<uint8_t*>(aPartsDest); job->partsCap = aPartsCap; job->partsHave = aPartsHave;
+ }
if (job->colsKnown) {
job->colX0 = std::max(0, std::min(aColX0, aWidth)); job->colX1 = std::max(0, std::min(aColX1, aWidth));
}
@@ -1075,13 +1082,14 @@ PuppetWidget::JihadPostJob(void* aDest, int aStride, int aWidth, int aHeight, in
uint32_t
PuppetWidget::JihadComposePost(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,
- const float* aView, bool aScrollValid, int aColX0, int aColX1)
+ const float* aView, bool aScrollValid, int aColX0, int aColX1,
+ void* aPartsDest, int aPartsCap, uint32_t aPartsHave)
{
if (!aDest || aWidth <= 0 || aHeight <= 0 || aStride < aWidth * 4) {
return 0;
}
return JihadPostJob(aDest, aStride, aWidth, aHeight, aRowLo, aRowHi, aRow2Lo, aRow2Hi, aShift, aSrc, aSrcY, aFlags,
- aView, aScrollValid, aColX0, aColX1);
+ aView, aScrollValid, aColX0, aColX1, aPartsDest, aPartsCap, aPartsHave);
}
int
@@ -1144,7 +1152,12 @@ PuppetWidget::JihadComposePoll(uint32_t aToken, bool aWait, int* aOut, bool* aSc
for (int k = 0; k < job->req.nStuck && k < 4; k++)
for (int c = 0; c < 4; c++) aOut[13 + 4 * k + c] = job->req.stuck[k][c];
}
- if (aOutLen >= 30) aOut[29] = (int)job->req.partsGen; // T-505
+ if (aOutLen >= 30) aOut[29] = (int)job->req.partsGen; // T-505: the parts' signature
+ if (aOutLen >= 49) { // T-505 (E2): the parts pass's bytes, the flags, the card's hold set
+ aOut[30] = job->req.partsBytes; aOut[31] = job->req.partsFlags; aOut[32] = job->req.nHold;
+ for (int k = 0; k < 4; k++)
+ for (int c = 0; c < 4; c++) aOut[33 + 4 * k + c] = k < job->req.nHold ? job->req.hold[k][c] : 0;
+ }
}
if (aScrollOnly) *aScrollOnly = job->req.scrollOnly;
} else if (!aWait && job->req.composed) {
@@ -1522,7 +1535,8 @@ MOZ_EXPORT int
jihad_offscreen_overview_ok()
{
static const bool sPass = getenv("JIHAD_GPU_PASS") != nullptr;
- return sPass ? 0 : 2; // 2 (T-505): flag 128 too, and the poll's 30th int (the parts' generation)
+ return sPass ? 0 : 3; // 2 (T-505): flag 128 too, and the poll's 30th int; 3 (E2): compose_post2/start2's parts
+ // pass and the poll's 49 ints
}
MOZ_EXPORT int
jihad_offscreen_compose_peek(nsIWidget* aWidget, uint32_t aToken)
@@ -1562,6 +1576,38 @@ jihad_offscreen_compose_start(nsIWidget* aWidget, nsIDocShell* aDocShell, void*
return static_cast<PuppetWidget*>(aWidget)->JihadComposeStart(aDocShell, aDest, aStride, aWidth, aHeight, aRowLo, aRowHi, aRow2Lo, aRow2Hi, aShift, aSrc, aSrcY, aEngineShift,
(aEngineShift & 4) ? aColX0 : 0, (aEngineShift & 4) ? aColX1 : 0);
}
+// T-505 (E2): post and start with a frame without its parts' pass arguments: where its parts' picture goes (aPartsCap
+// bytes), and the signature of the picture the daemon holds (a pass only when this frame's differs)
+MOZ_EXPORT uint32_t
+jihad_offscreen_compose_post2(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,
+ double aOffX, double aOffY, double aScrollX, double aScrollY, int aScrollValid,
+ int aColX0, int aColX1, void* aPartsDest, int aPartsCap, uint32_t aPartsHave)
+{
+ if (!aWidget) {
+ return 0;
+ }
+ const float view[5] = { (float)aScale, (float)aOffX, (float)aOffY, (float)aScrollX, (float)aScrollY };
+ return static_cast<PuppetWidget*>(aWidget)->JihadComposePost(aDest, aStride, aWidth, aHeight, aRowLo, aRowHi,
+ aRow2Lo, aRow2Hi, aShift, aSrc, aSrcY, aFlags,
+ aUseView ? view : nullptr, aScrollValid != 0,
+ (aFlags & 4) ? aColX0 : 0, (aFlags & 4) ? aColX1 : 0,
+ aPartsDest, aPartsCap, aPartsHave);
+}
+MOZ_EXPORT uint32_t
+jihad_offscreen_compose_start2(nsIWidget* aWidget, nsIDocShell* aDocShell, void* aDest, int aStride,
+ int aWidth, int aHeight, int aRowLo, int aRowHi, int aRow2Lo, int aRow2Hi, int aShift,
+ const void* aSrc, int aSrcY, int aEngineShift, int aColX0, int aColX1,
+ void* aPartsDest, int aPartsCap, uint32_t aPartsHave)
+{
+ if (!aWidget) {
+ return 0;
+ }
+ return static_cast<PuppetWidget*>(aWidget)->JihadComposeStart(aDocShell, aDest, aStride, aWidth, aHeight, aRowLo, aRowHi, aRow2Lo, aRow2Hi, aShift, aSrc, aSrcY, aEngineShift,
+ (aEngineShift & 4) ? aColX0 : 0, (aEngineShift & 4) ? aColX1 : 0,
+ aPartsDest, aPartsCap, aPartsHave);
+}
MOZ_EXPORT int
jihad_offscreen_compose_poll(nsIWidget* aWidget, uint32_t aToken, int* aOut, int* aScrollOnly)
{
diff --git a/widget/PuppetWidget.h b/widget/PuppetWidget.h
index c0331d09a7..5378b5afa0 100644
--- a/widget/PuppetWidget.h
+++ b/widget/PuppetWidget.h
@@ -292,7 +292,8 @@ public:
uint32_t JihadComposeStart(nsIDocShell* aDocShell, void* aDest, int aStride,
int aWidth, int aHeight, int aRowLo, int aRowHi, int aRow2Lo = 0, int aRow2Hi = 0, int aShift = 0,
const void* aSrc = nullptr, int aSrcY = 0, int aFlags = 0, // aFlags: 1 engine shift, 2 verify, 4 columns
- int aColX0 = 0, int aColX1 = 0); // T-511: the bands' stale columns (flag 4)
+ int aColX0 = 0, int aColX1 = 0, // T-511: the bands' stale columns (flag 4)
+ void* aPartsDest = nullptr, int aPartsCap = 0, uint32_t aPartsHave = 0); // T-505 (E2)
// aOut (10 ints, may be null): the rows the tree changed, the rows fixed layers cover, the two
// bands read, the frame's position and its valid flag; 12 for a job posted with flag 4 (T-511):
// the columns the composite changed; aScrollOnly: the composite's only change was the card's scroll
@@ -305,7 +306,8 @@ public:
bool JihadPaintLayers(nsIDocShell* aDocShell);
uint32_t JihadComposePost(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,
- const float* aView, bool aScrollValid, int aColX0 = 0, int aColX1 = 0);
+ const float* aView, bool aScrollValid, int aColX0 = 0, int aColX1 = 0,
+ void* aPartsDest = nullptr, int aPartsCap = 0, uint32_t aPartsHave = 0); // T-505 (E2)
void JihadComposeForget(uint32_t aToken);
// T-515: a job's state without taking its result: 1 done, 2 composed (being read), 0 pending, -1 unknown
int JihadComposePeek(uint32_t aToken);
@@ -468,7 +470,8 @@ private:
mozilla::Mutex mJihadJobsLock{"jihad-compose-jobs"}; // T-515: the list from the main thread and the express lane
uint32_t JihadPostJob(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,
- const float* aView = nullptr, bool aScrollValid = false, int aColX0 = 0, int aColX1 = 0);
+ const float* aView = nullptr, bool aScrollValid = false, int aColX0 = 0, int aColX1 = 0,
+ void* aPartsDest = nullptr, int aPartsCap = 0, uint32_t aPartsHave = 0);
uint32_t mJihadNextToken = 1;
float mJihadScrollX = 0.0f, mJihadScrollY = 0.0f; // T-502: the card's scroll for the compositor
bool mJihadScrollValid = false;