T-513/T-499: the review of Tier 87 fixed and verified, Codex's nine on top; the layout viewport per document (cavekit-gpu-rendering R2, R10; cavekit-modern-web R6)

T-513. Two inspectors reviewed Tier 87 (Codex was out of credits) and found twelve problems, all fixed
(context/impl/impl-review-findings-gpu-2.md). The engine's are engine 0058 (the band scratch per
compositor, every closed card's pbuffers and GL objects freed, the frame surface current for a stacked
band frame, WebGL textures externally owned, given-up reads counted and never copied into a slot, the
regions cap, the sticky probe's reach); the daemon's: the buffer the card shows is never composed into,
the express jobs forgotten on a GPU fallback, a job of another frame size given up, the adapter's window
check, the inject threads joined. Twenty card cycles add 76 KB (R10, was +16 MB a card).

R2's new criterion passes: a fast flick (40 verified frames, shifts to 1210 rows) and two cards panned
in turn (22 frames) with 0 rows wrong. JIHAD_GPU_VERIFY's reference is now composed right behind its
job on the same layer tree: composed at the job's poll it met a tree the flick had moved on from and
called right frames wrong. New round tools: tools/device/t502/two-cards.sh, the inject channel's
"target focus|last", the Enyo hook's A step.

Codex then reviewed all of it and found nine more, all fixed: the opt-in read paths' late writes (engine),
the previously shown buffer's in-flight mark restarted on each publish, the verify buffers a pool never
resized or reused while pending, the document key the inner window's id, the adapter's window bound by
division, finished inject threads reaped, and the spike scripts' daemon waits and restore order.

T-499. The card's own size resent keeps a followed layout viewport; the layout viewport is the
document's (a new document starts at the card's size: a device-width page after a followed one was laid
out at 1000 and shown at 0.77); the rule waits until the document is parsed and never follows
about:blank (mid-parse the engine lays a page out at 980 as if it had no meta viewport). Probe page with
rotation and navigation, BBC, YouTube mobile, Wikipedia, DuckDuckGo: docs/rounds/t499-2026-10-19/.

The three interfaces pass their default checks on this build (GPU path, express lane, no fault or
fallback); the bench runs it with the rebuilt adapters. Engine 0058 is in the patch queue and the pins
moved to it (build/engine/engine-version.env, the OE recipe, the gitlink, ENGINE-SOURCE's row). The kits'
criteria this met (gpu R2's flick and two-card criterion, R10's twenty cycles, modern-web's T-499 line)
and PICKUP's night delta with the traps (the bench's Wi-Fi outage, prefs saved over by an exiting daemon)
land with the Tier 88 commit that follows, whose kit edits share hunks with these.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
NotAlexNoyleandClaude Opus 5.5 committed 2026-10-24 13:49:04 -04:00
1 parent 506c41144d
commit 3f4e1a27e8
54 files changed
+1277 -86

No files matched your search

@@ -0,0 +1,368 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NotAlexNoyle <alexnoyle@icloud.com>
Date: Sat, 24 Oct 2026 13:45:33 -0400
Subject: [PATCH] gpu compositor: the review of Tier 87 fixed (scratch per
compositor, GL objects freed per card, late reads)
T-513, the review of the GPU path (two inspectors; context/impl/impl-review-findings-gpu-2.md, E1-E7):
- E1: the band scratch's size is the compositor's own (mJihadBandScratchSize), not a function-static
shared by all: with two cards one was handed a buffer another had outgrown and glReadPixels wrote
past it.
- E2: a closed card's frame-sized and band pbuffers are destroyed with its context, and
CompositorOGL::CleanupResources deletes the window targets', the band target's and the swizzle
pass's renderbuffers, framebuffers, texture and program: in the process-long share group each closed
card left ~13-16 MB of GPU memory. Twenty open/close cycles now add 76 KB.
- E3: a band frame of 257-320 rows stacked in the frame-sized target makes that surface current first
(on the Adreno 220 every target must match the current surface).
- E4: a WebGL canvas's texture source is externally owned (the producer's SharedSurface deletes the
name; in one share group the second delete could hit a name handed out again).
- E5: the reads carry their request's cancel flag (JihadSetCancel): a read given up past the daemon's
bound stops after glReadPixels, before the swizzle and any copy into the slot; a whole read (straight
into the slot) is counted, jihad_offscreen_late_writes(), and the daemon then paints every slot whole.
- E6: a frame whose chunks would pass the 12-band cap of one composite takes the whole path instead
of reading chunks it did not compose.
- E7: a sticky layer's stuck probe reaches twice the composite's scroll step (64 to 1024 px), so a fast
flick's step no longer slides its old pixels with the content.
Codex on the fixes (2026-10-24, C1): the opt-in read paths stop for a given-up frame as well: the
scratch read before its copy into the slot, the swizzle pass and the read thread after their read
(counted as late writes), the read thread also before it, and a cancelled read-thread frame is not
reported good.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---
gfx/gl/GLContextProviderEGL.cpp | 4 ++
.../composite/AsyncCompositionManager.cpp | 13 +++-
.../composite/AsyncCompositionManager.h | 2 +
gfx/layers/ipc/CompositorBridgeParent.cpp | 15 ++++-
gfx/layers/opengl/CompositorOGL.cpp | 61 +++++++++++++++++--
gfx/layers/opengl/CompositorOGL.h | 5 ++
gfx/layers/opengl/TextureHostOGL.cpp | 6 +-
widget/PuppetWidget.cpp | 8 +++
8 files changed, 107 insertions(+), 7 deletions(-)
diff --git a/gfx/gl/GLContextProviderEGL.cpp b/gfx/gl/GLContextProviderEGL.cpp
index af1f7fb58c..eb00bc01f0 100644
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -259,6 +259,10 @@ GLContextEGL::~GLContextEGL()
sEGLLibrary.fDestroyContext(EGL_DISPLAY(), mContext);
sEGLLibrary.UnsetCachedCurrentContext();
+ // Jihad (T-513 review): the frame-sized and the band pbuffers made for this context
+ if (mJihadBandPb && mJihadBandPb != mSurface) DestroySurface(mJihadBandPb);
+ if (mJihadPbOverride && mJihadPbOverride != mSurface && mJihadPbOverride != mJihadBandPb) DestroySurface(mJihadPbOverride);
+ mJihadBandPb = mJihadPbOverride = nullptr;
DestroySurface(mSurface);
}
diff --git a/gfx/layers/composite/AsyncCompositionManager.cpp b/gfx/layers/composite/AsyncCompositionManager.cpp
index 9fdd72247c..ef58b6b74b 100644
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -558,7 +558,7 @@ AsyncCompositionManager::AlignFixedAndStickyLayers(Layer* aTransformedSubtreeRoo
const LayerRect& outer = layer->GetStickyScrollRangeOuter();
const LayerRect& inner = layer->GetStickyScrollRangeInner();
const LayerPoint base = translation - unconsumedTranslation; // the translation before the sticky clamp
- for (const float probe : { 64.0f, -64.0f }) {
+ for (const float probe : { mJihadProbe, -mJihadProbe }) {
const float ty = base.y + probe;
const float hy = IntervalOverlap(ty, outer.y, outer.YMost()) - IntervalOverlap(ty, inner.y, inner.YMost());
if (fabsf(hy) > 0.5f) held = true;
@@ -863,6 +863,17 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer,
bool* aOutFoundRoot)
{
mJihadStuck.clear(); // Jihad T-502 round 127: this composite's stuck sticky layers
+ {
+ // T-513 (review): a sticky layer is probed as far as twice this composite's scroll step (64 px at
+ // least): a fast flick crossed the threshold by more than a fixed 64 px in one frame, its stuck
+ // position was not read and its old pixels slid with the content as a ghost strip
+ // (a frame may compose more than once at one scroll: only a move sets it)
+ const float step = mJihadScrollValid ? fabsf(mJihadScrollY - mJihadPrevScrollY) : 0.0f;
+ if (step > 0.5f) {
+ mJihadProbe = std::max(64.0f, std::min(2.0f * step, 1024.0f));
+ mJihadPrevScrollY = mJihadScrollY;
+ }
+ }
bool appliedTransform = false;
std::stack<Maybe<ParentLayerIntRect>> stackDeferredClips;
diff --git a/gfx/layers/composite/AsyncCompositionManager.h b/gfx/layers/composite/AsyncCompositionManager.h
index 459be6fb09..77c439eca5 100644
--- a/gfx/layers/composite/AsyncCompositionManager.h
+++ b/gfx/layers/composite/AsyncCompositionManager.h
@@ -239,6 +239,8 @@ private:
bool mIsFirstPaint;
float mJihadScrollX = 0.0f, mJihadScrollY = 0.0f; // Jihad T-502
bool mJihadScrollValid = false;
+ float mJihadPrevScrollY = 0.0f; // T-513: the card's scroll at the composite before
+ float mJihadProbe = 64.0f; // T-513: how far a sticky layer is probed for sticking (layer px)
std::vector<Layer*> mJihadStuck; // round 127 (raw: valid until the next composite)
// This flag is set during a layers update, so that the first composition
diff --git a/gfx/layers/ipc/CompositorBridgeParent.cpp b/gfx/layers/ipc/CompositorBridgeParent.cpp
index 4cf93e1414..4650113654 100644
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -1969,7 +1969,9 @@ CompositorBridgeParent::JihadComposeAndRead(JihadFrameRequest* aReq, float aScal
JihadChunk chunks[12];
int nChunks = 0;
const int chunkRows = JihadPackColumns(aReq->cols, aReq->nCols, fullAsked, ogl->JihadBandRows(), aWidth, chunks, &nChunks);
- const bool fits = chunkRows >= 0 && fullAsked + chunkRows <= bandMax;
+ // T-513 (review): the regions of one composite are at most 12, bands included: chunks past that
+ // would be read without being composed, so such a frame does not fit
+ const bool fits = chunkRows >= 0 && fullAsked + chunkRows <= bandMax && nChunks + (b1 ? 1 : 0) + (b2 ? 1 : 0) <= 12;
if ((scrollOnly || diffFirst) && bandMax > 0 && (b1 || b2) && fits && aReq->dest && aReq->stride == aWidth * 4 &&
!getenv("JIHAD_GPU_READ_THREAD")) {
// Both bands stacked in the band target (band 2 under band 1), composed one after the
@@ -1984,6 +1986,13 @@ CompositorBridgeParent::JihadComposeAndRead(JihadFrameRequest* aReq, float aScal
// stack at the top of the frame-sized target (round 105)
const TimeStamp tp0 = TimeStamp::Now();
const int passRows = (top > 0 && top <= ogl->JihadBandRows()) ? ogl->JihadBeginBandPass(aWidth) : 0;
+ if (!passRows) {
+ // T-513 (review): stacked at the top of the frame-sized target, so the frame's own surface
+ // current (a band frame before this one left the band pbuffer current, and on the Adreno 220
+ // every target must match the current surface's size: memory compositor-scroll-traps)
+ ogl->JihadUseFrameSurface();
+ ogl->JihadEnsureTargetSize(aWidth, aHeight);
+ }
const double msBegin = (TimeStamp::Now() - tp0).ToMilliseconds();
const gfx::IntRect passBounds = passRows ? gfx::IntRect(0, 0, aWidth, passRows) : whole;
bool ok = top > 0 && top <= bandMax;
@@ -2067,6 +2076,9 @@ CompositorBridgeParent::JihadComposeAndRead(JihadFrameRequest* aReq, float aScal
if (aReq->cancelled) ok = false; else ogl->JihadShiftRowsNow(aReq); // T-509: given up, dest untouched
msShift = (TimeStamp::Now() - c1).ToMilliseconds();
}
+ // T-513 (review): the reads below belong to a request the daemon may give up meanwhile
+ struct CancelScope { CompositorOGL* o; ~CancelScope() { o->JihadSetCancel(nullptr); } } cancelScope{ogl};
+ 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 &&
@@ -2081,6 +2093,7 @@ CompositorBridgeParent::JihadComposeAndRead(JihadFrameRequest* aReq, float aScal
TimeStamp r0 = TimeStamp::Now();
uint8_t* scratch = fullRows ? ogl->JihadBandScratch(aWidth * 4 * fullRows) : nullptr;
if (fullRows) ok = scratch && ogl->JihadReadFrame(scratch, aReq->stride, aWidth, passRows ? passRows : aHeight, 0, fullRows);
+ if (ok && fullRows && aReq->cancelled) ok = false; // T-513 (review): given up: the slot not touched
if (ok && fullRows) {
int from = 0;
for (int b = 0; b < 2; b++) {
diff --git a/gfx/layers/opengl/CompositorOGL.cpp b/gfx/layers/opengl/CompositorOGL.cpp
index c027bad4e5..21dd27b6fe 100644
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -38,6 +38,7 @@
#include "mozilla/layers/Effects.h" // for EffectChain, TexturedEffect, etc
#include "mozilla/layers/TextureHost.h" // for TextureSource, etc
#include "mozilla/layers/TextureHostOGL.h" // for TextureSourceOGL, etc
+#include <atomic> // Jihad T-513: gJihadLateWrites
#include "mozilla/mozalloc.h" // for operator delete, etc
#include "nsAppRunner.h"
#include "nsAString.h"
@@ -243,6 +244,29 @@ CompositorOGL::CleanupResources()
mGLContext->MakeCurrent();
+ // Jihad (T-513 review): the window targets, the band target, the swizzle pass's objects. Every
+ // GPU-path context shares the process-long share root's group (engine 0051), so what is not
+ // deleted here outlives the card: ~13 MB of GPU memory each card closed.
+ for (int t = 0; t < 2; ++t) {
+ if (mJihadTargetFBO[t]) mGLContext->fDeleteFramebuffers(1, &mJihadTargetFBO[t]);
+ if (mJihadTargetRB[t]) mGLContext->fDeleteRenderbuffers(1, &mJihadTargetRB[t]);
+ mJihadTargetFBO[t] = mJihadTargetRB[t] = 0;
+ }
+ if (mJihadWindowTex) { // JIHAD_GPU_PASS: the window target is a texture's FBO of its own
+ if (mJihadWindowFBO) mGLContext->fDeleteFramebuffers(1, &mJihadWindowFBO);
+ mGLContext->fDeleteTextures(1, &mJihadWindowTex);
+ mJihadWindowTex = 0;
+ }
+ mJihadWindowFBO = 0;
+ if (mJihadBandFBO) mGLContext->fDeleteFramebuffers(1, &mJihadBandFBO);
+ if (mJihadBandRB) mGLContext->fDeleteRenderbuffers(1, &mJihadBandRB);
+ mJihadBandFBO = mJihadBandRB = 0;
+ if (mJihadReadFBO) mGLContext->fDeleteFramebuffers(1, &mJihadReadFBO);
+ if (mJihadReadRB) mGLContext->fDeleteRenderbuffers(1, &mJihadReadRB);
+ mJihadReadFBO = mJihadReadRB = 0;
+ if (mJihadSwizzleProg) mGLContext->fDeleteProgram(mJihadSwizzleProg);
+ mJihadSwizzleProg = 0;
+
mBlitTextureImageHelper = nullptr;
mContextStateTracker.DestroyOGL(mGLContext);
@@ -2353,13 +2377,18 @@ CompositorOGL::JihadUseFrameSurface()
}
}
+// T-513 (review): reads of a given-up frame that had already written into its slot (a whole read goes
+// straight there); the daemon repaints when this moves (jihad_offscreen_late_writes, PuppetWidget.cpp)
+std::atomic<uint32_t> gJihadLateWrites(0);
+
uint8_t*
CompositorOGL::JihadBandScratch(size_t aBytes)
{
- static size_t sHave = 0;
- if (!mJihadBandScratch || sHave < aBytes) {
+ // T-513 (review): the size is this compositor's, as the buffer is (one compositor per card, all on
+ // the one compositor thread: a size shared between them handed a card a buffer another had outgrown)
+ if (!mJihadBandScratch || mJihadBandScratchSize < aBytes) {
mJihadBandScratch = MakeUnique<uint8_t[]>(aBytes);
- sHave = aBytes;
+ mJihadBandScratchSize = aBytes;
}
return mJihadBandScratch.get();
}
@@ -2375,6 +2404,8 @@ CompositorOGL::JihadReadAsync(JihadFrameRequest* aReq)
}
aReq->composed = true;
if (!JihadEnsureReadThread()) {
+ struct CancelScope { CompositorOGL* c; ~CancelScope() { c->JihadSetCancel(nullptr); } } cancelScope{this};
+ JihadSetCancel(&aReq->cancelled); // T-513 (review)
const TimeStamp t0 = TimeStamp::Now();
if (aReq->shift || aReq->src) mGLContext->fFlush(); // the GPU starts the composite while the rows are copied
JihadShiftRows(aReq);
@@ -2499,6 +2530,7 @@ CompositorOGL::JihadReadOnReadThread(JihadFrameRequest* aReq)
if (lo < 0) lo = 0;
if (hi > h) hi = h;
if (hi <= lo || !aReq->dest || aReq->stride != w * 4) continue;
+ if (aReq->cancelled) break; // T-513 (Codex): given up: no read into the slot
const int rows = hi - lo;
uint8_t* band = aReq->dest + (size_t)lo * aReq->stride;
gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mJihadReadTargetFBO[aReq->target]);
@@ -2508,6 +2540,10 @@ CompositorOGL::JihadReadOnReadThread(JihadFrameRequest* aReq)
gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 4);
gl->fReadPixels(0, h - hi, w, rows, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, band);
}
+ if (aReq->cancelled) { // given up during the read: it wrote into the slot (counted, as on the compositor thread)
+ ++gJihadLateWrites;
+ break;
+ }
t2 = TimeStamp::Now();
JihadSwizzleAndFlipRows(band, aReq->stride, w, rows, true);
static int sTimed = 0;
@@ -2516,7 +2552,7 @@ CompositorOGL::JihadReadOnReadThread(JihadFrameRequest* aReq)
(t1 - t0).ToMilliseconds(), waitCpuMs, rows, (t2 - t1).ToMilliseconds(), (TimeStamp::Now() - t2).ToMilliseconds());
}
}
- ok = true;
+ ok = !aReq->cancelled; // T-513 (Codex): a given-up frame is not a good one
}
{
MonitorAutoLock lock(mJihadReadMon);
@@ -2579,6 +2615,11 @@ CompositorOGL::JihadReadFrame(uint8_t* aDest, int aStride, int aWidth, int aHeig
if (blend) gl->fEnable(LOCAL_GL_BLEND);
if (scissor) gl->fEnable(LOCAL_GL_SCISSOR_TEST);
gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mJihadWindowFBO);
+ if (mJihadCancel && *mJihadCancel) {
+ // T-513 (Codex): the opt-in swizzle pass reads straight into the slot too: counted, as below
+ if (aDest != mJihadBandScratch.get()) ++gJihadLateWrites;
+ return false;
+ }
if (sTimed++ < 12 || (sTimed % 100) == 0) {
JIHAD_GPU_LOG("[jihad-gpu] swizzle pass %.1f ms (issued), glReadPixels %d rows %.1f ms\n",
(t1 - t0).ToMilliseconds(), rows, (TimeStamp::Now() - t1).ToMilliseconds());
@@ -2605,6 +2646,9 @@ CompositorOGL::JihadReadFrame(uint8_t* aDest, int aStride, int aWidth, int aHeig
if (sScratch) {
uint8_t* tmp = JihadBandScratch((size_t)aStride * rows);
mGLContext->fReadPixels(0, h - hi, w, rows, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, tmp);
+ if (mJihadCancel && *mJihadCancel) {
+ return false; // T-513 (Codex): given up while the GPU was read; the slot is not touched
+ }
memcpy(band, tmp, (size_t)aStride * rows);
} else if (sChunk > 0) {
for (int done = 0; done < rows; done += sChunk) {
@@ -2640,6 +2684,12 @@ CompositorOGL::JihadReadFrame(uint8_t* aDest, int aStride, int aWidth, int aHeig
}
}
}
+ if (mJihadCancel && *mJihadCancel) {
+ // T-513 (review): given up while the GPU was read; the daemon may paint this slot in software
+ // already. Rows read into the slot itself are the daemon's to repaint
+ if (aDest != mJihadBandScratch.get()) ++gJihadLateWrites;
+ return false;
+ }
TimeStamp t1 = TimeStamp::Now();
JihadSwizzleAndFlipRows(band, aStride, w, rows, true);
{
@@ -2717,6 +2767,9 @@ CompositorOGL::JihadReadColumns(uint8_t* aDest, int aStride, int aSurfaceH, int
mGLContext->fReadPixels(aX0, aSurfaceH - (aSrcRowLo + aRows), w, aRows, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, sBuf);
}
const TimeStamp t1 = TimeStamp::Now();
+ if (mJihadCancel && *mJihadCancel) {
+ return false; // T-513 (review): given up while the GPU was read; the frame is not touched
+ }
JihadSwizzleAndFlipRows(sBuf, w * 4, w, aRows, true);
for (int r = 0; r < aRows; r++) {
memcpy(aDest + (size_t)(aDestRowLo + r) * aStride + (size_t)destX * 4, sBuf + (size_t)r * w * 4, (size_t)w * 4);
diff --git a/gfx/layers/opengl/CompositorOGL.h b/gfx/layers/opengl/CompositorOGL.h
index 4b735ac840..a4c738dc47 100644
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -250,6 +250,9 @@ public:
int JihadBeginBandPass(int aWidth);
void JihadEndBandPass();
void JihadUseFrameSurface(); // the frame's pbuffer current again (before a whole composite)
+ // T-513 (review): the reads that follow belong to a request the daemon may give up while the GPU
+ // is read (a stall shows up inside glReadPixels); null when they are done
+ void JihadSetCancel(const mozilla::Atomic<bool>* aCancel) { mJihadCancel = aCancel; }
GLuint mJihadBandRB = 0, mJihadBandFBO = 0;
int mJihadBandW = 0;
// the band surface's rows: JIHAD_GPU_BAND_ROWS (64..942, default 256; round 137)
@@ -426,6 +429,8 @@ private:
bool mJihadProbed = false;
gfx::IntSize mJihadTargetSize; // the window targets' storage size (round 101)
UniquePtr<uint8_t[]> mJihadBandScratch; // one read of the stacked bands, split from here
+ size_t mJihadBandScratchSize = 0; // its size (T-513: per compositor)
+ const mozilla::Atomic<bool>* mJihadCancel = nullptr; // T-513: the frame being read was given up (JihadSetCancel)
int mJihadCur = 0; // the target the next frame draws into
mozilla::Atomic<int32_t> mJihadTargetBusy[2]; // handed to the read thread, not yet read
mozilla::Monitor mJihadReadMon{"jihad-read"};
diff --git a/gfx/layers/opengl/TextureHostOGL.cpp b/gfx/layers/opengl/TextureHostOGL.cpp
index d73926a1c6..126f5b4c60 100644
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -572,12 +572,16 @@ GLTextureHost::Lock()
if (!mTextureSource) {
gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::R8G8B8A8
: gfx::SurfaceFormat::R8G8B8X8;
+ // Jihad (T-513 review): the name is the client's, as the comment always said: the producer's
+ // SharedSurface_GLTexture deletes it on its own context. Deleted here too, in the GPU path's one
+ // share group (engine 0051) a name handed out again in between (a tile texture, a WebGL
+ // texture) was deleted under its new owner.
mTextureSource = new GLTextureSource(mCompositor,
mTexture,
mTarget,
mSize,
format,
- false /* owned by the client */);
+ true /* owned by the client */);
}
return true;
diff --git a/widget/PuppetWidget.cpp b/widget/PuppetWidget.cpp
index 4e40a83508..b056433871 100644
--- a/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -37,6 +37,7 @@
#include "mozilla/gfx/JihadGpuLog.h" // Jihad (T-512): JIHAD_GPU_LOG
namespace mozilla { namespace gl { extern std::atomic<uint32_t> gJihadUploads; extern std::atomic<uint64_t> gJihadUploadUs; } } // T-517, GLUploadHelpers.cpp
+namespace mozilla { namespace layers { extern std::atomic<uint32_t> gJihadLateWrites; } } // T-513, CompositorOGL.cpp
#include "base/task.h"
// Jihad offscreen render-document support:
#include "gfxContext.h"
@@ -1453,6 +1454,13 @@ jihad_offscreen_gpu_given_up()
{
sJihadGpuGivenUp = true;
}
+// T-513 (review): how many reads of a given-up frame had already written into its slot (a GPU stall
+// inside glReadPixels outlasting the daemon's bound); the daemon repaints in software when it moves
+MOZ_EXPORT uint32_t
+jihad_offscreen_late_writes()
+{
+ return mozilla::layers::gJihadLateWrites;
+}
MOZ_EXPORT bool
jihad_gpu_compositor_on()
{
+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=baaefa5889598a67b31546e4ed0a6e49a6a41436
UXP_PIN=7a872e8f08d5939c4535e139aee8f51411c8cc88
# 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 = "baaefa5889598a67b31546e4ed0a6e49a6a41436"
SRCREV = "7a872e8f08d5939c4535e139aee8f51411c8cc88"
S = "${WORKDIR}/git"
# Modern C++14 toolchain (device-build R1) — the stock dylan/device gcc cannot build UXP.
+2
View File
@@ -23,3 +23,5 @@ Build site: context/plans/build-site.md
| T-508 | DONE | Rounds 213-228. `build/webos-oe/push-engine-update.sh` (FFmpeg's libraries), `render/goanna/test/jihad-video-styled.html`, `jihad-anim-opacity.html`, `jihad-canplay.html`. Measured on the device: four codecs to the end, styled video, Flash anim and keys. |
| T-509 | DONE | Rounds 229-232. `BrowserPageGoanna.cpp` (`gpuFallBack`, the refusal count, the job bound `JIHAD_GPU_JOB_BOUND_MS`), `GoannaRenderPage.{h,cpp}` (`DisableGpu`, the daemon-wide off switch, `ResetGpuResolution`), engine 0047, `tools/device/t502/three-daemons.sh` + `three-daemons-restore.sh`. |
| T-512 | DONE | `e8b0e425`: engine 0057 (`gfx/2d/JihadGpuLog.h` exported, the GPU path's files gated), `render/goanna/JihadGpuLog.h` (BrowserPageGoanna, GoannaRenderPage, JihadBrowserServer), the page-closed line and `default-check.sh`'s count, `gpu-spike-start.sh` `NOLOG`, `tools/device/t502/README.md` (every switch), `docs/RENDERING-OPTIONS.md`, `README.md`, `docs/PICKUP.md`, `docs/ENGINE-SOURCE.md`. |
| T-513 | DONE | 2026-10-19/24. Two inspectors (Codex out of credits) on Tier 87, twelve findings fixed; Codex by direct invocation on the fixes, T-499 and T-487 on 2026-10-24, nine more fixed: `context/impl/impl-review-findings-gpu-2.md`. Engine 0058 (`gfx/layers/opengl/CompositorOGL.{h,cpp}`, `GLContextProviderEGL.cpp`, `AsyncCompositionManager.{h,cpp}`, `CompositorBridgeParent.cpp`, `TextureHostOGL.cpp`, `PuppetWidget.cpp`); the daemon's `BrowserPageGoanna.{h,cpp}` (the shown buffer never composed into, the express jobs forgotten on a fallback, a job of another frame height given up, the verify reference posted behind its job from a static pool), `GoannaRenderPage.{h,cpp}` (`DocumentKey`/`ResetLayoutSize`), `JihadBrowserServer.{h,cpp}` (inject `target focus|last`, the inject threads reaped), `render/adapter/BrowserOffscreen.cpp` (the window bound). R2's fast flick and two cards (40 + 22 verified frames, 0 rows wrong), R10's twenty card cycles (+76 KB). Round notes `docs/rounds/t513-2026-10-19/`. |
| T-519 | DONE | 2026-10-24. `docs/rounds/t519-2026-10-24/` (14 pan logs, the dumps). New `render/goanna/test/jihad-fixed-both.html`; `tools/device/t502/pan-run.sh` (`ROUND_DIR`), `tools/device/t502/README.md` (the verify mode's two limits: an animating layer's rows, and its frame rates). Kit: R2 lines 2-3 and R4's four `[x]`, R2's first `[~]` (the animated block under verify), R3's GPU-against-software `[x]`; the table in `docs/RENDERING-OPTIONS.md`. Open: the verify reference's animation time pinned to its job's (engine). |
@@ -0,0 +1,68 @@
---
created: "2026-10-19"
last_edited: "2026-10-24"
---
# Review — Tier 87, the GPU path (T-513, 2026-10-19)
Build site: context/plans/build-site.md
Codex by direct invocation (Cavekit 3.1's own review prompt, `codex exec -m gpt-5.6-sol -s read-only`)
was tried first and refused both halves with its usage limit (until 17:54); per the kit (R11) and the
owner's standing rule the `ck:inspector` reviewed instead, read-only, in two halves run side by side:
the daemon and the adapter (`git diff ff7e1d20^ HEAD -- render/`, Tier 87's whole span) and the engine
(patches 0040-0057 of `build/desktop/patches`, read against the patched tree). The interim review of the
same day (`impl-review-findings-gpu.md`, seven findings) was given to both; neither repeats it, and
neither found one of its fixes wrong. Twelve findings, every one fixed.
## The engine (patches 0040-0057)
| # | Severity | Where | Finding | Disposition |
|---|---|---|---|---|
| E1 | P1 heap overflow | `CompositorOGL.cpp` `JihadBandScratch` | The scratch buffer is each compositor's, its size a function-static shared by all: with two cards, one card was handed a buffer another had outgrown and `glReadPixels` wrote past it | Fixed: the size is a member (`mJihadBandScratchSize`) |
| E2 | P1 leak | `GLContextProviderEGL.cpp` `~GLContextEGL`, `CompositorOGL::CleanupResources` | The frame-sized and band pbuffers were never destroyed, nor the window targets', band target's and swizzle pass's renderbuffers, framebuffers, texture and program; in the process-long share group (0051) that is ~13 MB of GPU memory per card closed | Fixed: the context destroys its two pbuffers; the compositor deletes its objects in `CleanupResources` |
| E3 | P2 wrong surface | `CompositorBridgeParent.cpp` band path | A band frame of 257-320 rows (over the band surface, under the band maximum) is stacked in the frame-sized target while the band pbuffer may still be current: on the Adreno 220 every target must match the current surface | Fixed: the frame's surface made current (and the targets sized) when no band pass is taken |
| E4 | P2 double delete | `TextureHostOGL.cpp` `GLTextureHost::Lock` | A WebGL canvas's texture name was deleted by the compositor's texture source and by the producer's `SharedSurface_GLTexture`; in one share group the second delete could hit a name handed out again (a tile, a WebGL texture) | Fixed: the source is made externally owned (the client's, as upstream's comment always said) |
| E5 | P2 late write | `CompositorOGL.cpp` reads, `CompositorBridgeParent.cpp` band path | `cancelled` was checked only before the shift and the read; a GPU stall shows up inside `glReadPixels`, so a frame given up past the 3 s bound could still be written into a slot the software path paints | Fixed: the reads carry the request's cancel flag (`JihadSetCancel`); after `glReadPixels` a cancelled read stops before the swizzle and before any copy into the slot; a whole read (straight into the slot) counts a late write, `jihad_offscreen_late_writes()`, and the daemon then paints every slot whole again |
| E6 | P3 stale columns | `CompositorBridgeParent.cpp` band path | The regions of one composite are capped at 12 bands included while up to 12 chunks were packed: chunks past the cap were read without being composed | Fixed: such a frame does not fit and takes the whole path |
| E7 | P3 ghost strip | `AsyncCompositionManager.cpp` sticky probe | A sticky layer counted as held only within a fixed 64 px of sticking; a fast flick's step crosses it by more, its stuck position was not read, its old pixels slid with the content | Fixed: probed as far as twice the composite's scroll step (64 px at least, 1024 at most), set only when the scroll moved |
## The daemon and the adapter
| # | Severity | Where | Finding | Disposition |
|---|---|---|---|---|
| D1 | P1 tearing on screen | `BrowserPageGoanna.cpp` `xStart`, `gpuJobStartNext` | An in-flight slot counted as free once its mark was 2 s old, and the adapter can return its own current buffer: after a still page, a zoom's express frames were composed into the buffer on screen, and every later step of the pinch too | Fixed: `mShownKey` (the last `msgPainted`'s key) is never composed into by the express lane or the main lane's second job |
| D2 | P2 write after fallback | `BrowserPageGoanna.cpp` `gpuFallBack` | The fallback forgot the main lane's jobs only; the express lane's stayed in the engine with their slot pointers (a recovering compositor writing over software frames, or into a detached segment) | Fixed: `xForget` of both express jobs first, while the GPU path is on; `ComposeForget` no longer gated on it |
| D3 | P2 wrong rows | `BrowserPageGoanna.cpp` main-lane poll | A job composed before the overscan turned on (a document's first scroll) was published at the new frame height: rows below its own from an older frame | Fixed: a job whose size differs from the tick's is given up with the jobs built on it; the next tick composes at the new size |
| D4 | P3 bounds | `render/adapter/BrowserOffscreen.cpp` `windowBytes` | The trailer's window check ran in 32-bit `size_t` and could wrap | Fixed: in 64 bits |
| D5 | P3 debug channel | `JihadBrowserServer.cpp` inject pan and zoom | Detached threads with raw pointers: a closed card's proxy address reused by a new card, `this` used after the server's destruction | Fixed: the card matched by proxy and page; the threads kept, stopped and joined in the destructor |
Found correct by the reviews: YapProxy's send pairs and locks, the lock order (frame lock, want lock,
wake lock), the reader thread's teardown, the two lanes' slot exclusions, the returned-buffer accounting,
the segment bounds of both lanes and the trailer; PuppetWidget's job list and monitors, every fixed-size
array of the band and whole paths, the row moves and reads, the swizzle, the regions (0055), the tile
cull (0056), the diff-first timing, the texture pool, the verify guard. Not reviewed deeply: the opt-in
read thread and swizzle pass.
Proposed and taken into the kit: R10, twenty card open/close cycles leave the GPU's and the daemon's
memory flat within a margin (the leak); R2, a fast flick (a shift over 256 rows a frame) and two cards
panned in turn under verify (the scratch and the surface findings).
## Codex on the fixes, T-499 and T-487 (2026-10-24)
Codex (`codex exec -m gpt-5.6-sol -s read-only`, Cavekit 3.1's review prompt) read the whole uncommitted
diff: the twelve fixes above, the verify mode's reference composed behind its job, the inject channel's
`target`, T-499's layout viewport per document and T-487's updater fixes. Nine findings, every one real,
every one fixed. (The inspector started the same review on 2026-10-19 and stopped at the weekly limit.)
| # | Severity | Where | Finding | Disposition |
|---|---|---|---|---|
| C1 | P1 | `CompositorOGL.cpp` reads | A given-up read could still reach the slot on the opt-in paths: the scratch read copied before the cancel check, the swizzle pass and the read thread had none | Fixed: the scratch read stops before its copy; the swizzle pass and the read thread check after their read and count a late write, the read thread also before it, and a cancelled read-thread frame is not good. (The default path's whole read lands in the slot by design and is counted: E5.) |
| C2 | P1 | `BrowserPageGoanna.cpp` publish | The buffer shown until a publish kept its old in-flight mark: on a still page older than the 2 s valve, the next job could take it while the adapter still blitted it | Fixed: `noteShown` restarts the previous buffer's mark when the card is told to show another |
| C3 | P2 | `BrowserPageGoanna.cpp` verify pool | The reference buffers were reused round robin with no record of a pending reference, and a resize could free one a compose still wrote into | Fixed: six buffers made once at the largest frame and never resized, each taken again only once polled or 5 s after it was given up; none free, the late reference |
| C4 | P2 | `GoannaRenderPage.cpp` `DocumentKey` | A document's address can be reused by the next | Fixed: the inner window's id (`currentInnerWindowID`) |
| C5 | P2 | `gpu-spike-start.sh` | It warned and went on when a daemon outlived the wait | Fixed: SIGKILL after 30 s, then the run does not start if one is left |
| C6 | P2 | `gpu-spike-collect-stop.sh` | The same on the way out | Fixed: as C5; the profile and the upstart job are left alone if one is left |
| C7 | P2 | `gpu-spike-collect-stop.sh` | The upstart daemon was started before the run's prefs and `goanna.js` were put back (it could save them back) | Fixed: every restore first, then the start |
| C8 | P3 | `BrowserOffscreen.cpp` `windowBytes` | 64 bits do not stop the product of three header values from wrapping | Fixed: the rows held against the room the raster has, by division |
| C9 | P3 | `JihadBrowserServer.cpp` inject threads | Finished threads were kept until shutdown | Fixed: the finished ones joined when the next starts |
+2 -2
View File
@@ -514,7 +514,7 @@ the mid-pan dumps, never by a screenshot. No device run while the owner is at th
| T-510 | The default: the GPU path on in all three event.d jobs with `JIHAD_GPU_COMPOSITOR=0` and a pref to force software; Mochi and Mojo load, pan, zoom and close on it; the startup report | cavekit-gpu-rendering.md | R1 | T-503, T-507, T-509, T-515 | yes | M | DONE 2026-10-19 (blocked by T-504 until then so the owner's zoom complaint would not ship; the express lane (T-515) closed it. The generated upstart jobs start the GPU path with the express lane; `JIHAD_GPU_COMPOSITOR=0` or the pref `jihad.gpu.enabled=false` (read before the engine starts) give the software path, and the log names the path taken; all three interfaces load, pan, zoom and close Wikipedia on their default daemons (`default-check.sh`), the pref and the env override checked on the bench) |
| T-511 | WebGL through EGL: contexts on the device's GPU on the GPU path, a spinning-cube test page ≥ 24 fps, context loss, spot conformance | cavekit-gpu-rendering.md | R8 | T-510 | yes | L | PARTIAL 2026-10-19 (rounds 281-313, `docs/rounds/t502-2026-10-17/webgl-r281-313.txt`; engine 0051 (WebGL on EGL on the GPU path, the stencil back mask, one share group so the canvas's texture is composited with no copy, a context released before its destruction), 0052 (the column read); the daemon's own-slot base judged by the recent changes. Met: contexts on the GPU path and none on the software path; the cube renders correctly; 8 open/close cycles and a lose/restore without a fault; the spot checks and the missing extensions listed. Open: the 480 px cube 18-20.5 fps on the card (240 px 26.5-26.9, 360 px 21.5-22.8): the compositor job's ~45 ms, carried by T-517; 2026-10-19 evening (round 358): the 480 px cube 19.5-20.9 fps on the card, the page's own rate 23.8-25.1; the card cannot pass the page's rate. BLOCKED with T-517's floor) |
| T-512 | Fit to keep: diagnostics pruned and bounded, the knobs listed, the engine work folded into the patch queue, the docs and README | cavekit-gpu-rendering.md | R11 | T-503…T-511, T-517 | no | M | DONE 2026-10-19 (`e8b0e425`: the `[jihad-gpu]` diagnostics behind one gate each in the engine (0057, `mozilla/gfx/JihadGpuLog.h`) and the daemon (`render/goanna/JihadGpuLog.h`), three startup lines in a shipped daemon, the default checks of all three interfaces pass; every switch in `tools/device/t502/README.md`; the 57 patches reproduce the pin and each has its ENGINE-SOURCE row; RENDERING-OPTIONS, the README and PICKUP carry the evening's numbers. The review (R11's third) is T-513) |
| T-513 | Review of Tier 87 (Codex by direct invocation, the inspector as backup) and fixes | cavekit-gpu-rendering.md | R11 | T-512 | no | M | pending |
| T-513 | Review of Tier 87 (Codex by direct invocation, the inspector as backup) and fixes | cavekit-gpu-rendering.md | R11 | T-512 | no | M | DONE 2026-10-19 (Codex out of credits: two inspectors, 12 findings, all fixed, `context/impl/impl-review-findings-gpu-2.md`; twenty card cycles flat (R10); a fast flick and two cards in turn pass the verify mode once its reference is composed behind the job (R2); the three default checks on the bench) |
| T-514 | [OWNER] Finger round on the GPU path in all three interfaces: pan, flick, pinch, double-tap | cavekit-gpu-rendering.md | R12 | T-513 | yes | S | BLOCKED-EXTERNAL (a person at the device) |
| T-515 | Frames made off the main thread: the card's scroll and zoom read on an IPC thread, the compositor composing and the frame published while the main thread runs the page's script or a paint (the open criteria of T-504, the first frame at a new zoom within 250 ms under script, and T-505, desktop Wikipedia ≥ 24 fps) | cavekit-gpu-rendering.md | R3, R6 | T-505 | yes | L | PARTIAL 2026-10-19 (rounds 240-267, the express lane: each card's YAP commands read on a thread of the proxy's own, the daemon's frame lock (`render/goanna/JihadFrameLock.h`; the main thread lets go of it in the pump and the layer paint), an express thread that composes the layer tree as it stands and publishes, engine 0048 (the paint and the compose post apart, jobs from any thread). Met: the zoom under script (Wikipedia after load 10/10 within 160-225 ms, held off median 681 and three never framed; a page holding the main thread 600 ms in 700 within 45-91 ms, held off median 450); pans on that page 26-32 fps (2.4-3.8); Wikipedia's 2.2 s restyle under frames every ~45 ms. Open, and no longer the main thread's: desktop Wikipedia 19-21 fps, the compositor thread's ~45 ms a frame (T-517); double taps 40-449 ms, GPU draws of the other resolution's tiles (T-516). `docs/rounds/t502-2026-10-17/express-r240-267.txt`) |
| T-516 | The zoom's first frame on every gesture: a zoom out after a zoom in's repaint draws the new resolution's tiles at ~0.55, 20-35 ms a draw on the Adreno (a 179 ms composite, round 263); found and removed, card double taps both ways within 250 ms in runs of ten | cavekit-gpu-rendering.md | R6 | T-515 | yes | M | PARTIAL 2026-10-19 (rounds 270-280: the slow frames were not draws: the first readback after a pause ran 100-390 ms (30-40 usual) with the CPU swizzle 20-122 ms (7 usual): the clocks after two idle seconds (the CPU floor released, the second core unplugged, the GPU idled down) and the engine's other threads. Kept: the YAP reader raises the clock hold on a scroll, zoom or smart-zoom request (`JIHAD_CLOCK_HOLD_MS`), the compositor and express threads at nice -6 (`JIHAD_GPU_FRAME_NICE`), the clocks on the timeline. Double taps (12 a run): before 4-8 over 250 ms (max 479), after 0-2 (261-323; median 158). Tried and dropped: an 8 s hold (no change: the GPU still idles), a warm-up frame on the smart-zoom request (its own slow frame queued ahead of the zoom's: worse). Open: the GPU's ramp after a pause) |
@@ -554,7 +554,7 @@ loading the modern web." Kit: cavekit-modern-web.md R1–R5.
| T-496 | The identity read on the wire and the second signature: `render/goanna/test/jihad-identity-server.py` (a host-side site that fakes `cf-mitigated: challenge` and Startpage's `/sp/captcha-block` redirect, logs every request's header, and has pages that report `navigator.userAgent`); the component's `_challengeOf` with the redirect signature; the no-loop branch, the frames' header and the third-party frame measured; eBay and old Reddit shown not to be the identity's (curl from the host, both tokens); daemon 12's console line carries the column (`source:line:column`) for the `NS_ERROR_NOT_INITIALIZED` bundles | cavekit-modern-web.md | R3, R1 | T-495 | yes | M | DONE (2026-10-16 12:13–12:25 on Enyo: every box of R3 ticked but Startpage's search, which meets Anubis's proof of work; `docs/MODERN-WEB.md` T-496) |
| T-497 | YouTube sped up (the owner 2026-10-16: "speed up youtube"): the desktop watch page measured against the mobile site on the host (13.7 MB vs 3.0 MB of script) and on the device (worst stall 24.4 s vs 5.3 s, RSS 274 vs 162 MB, the home feed in 18 s); `jihad.ua.sites` rules in `components/jihadUserAgent.js` (header + docShell pin), youtube.com → a mobile Firefox identity; the daemon's youtube.com row out; Settings → General "Sites with a set identity" | cavekit-modern-web.md | R3, R6 | T-495 | yes | M | DONE (2026-10-16; the ledger T-497; the tap on the mobile player is a finger test) |
| T-498 | The web compatibility shims (the owner 2026-10-16: "fix the regular duckduckgo"): `components/jihadWebCompat.js` — a sandbox per document defines what the engine lacks (CSSStyleSheet.replace, popover, inert, Intl.ListFormat, reportError, checkVisibility, AbortSignal.any, URLSearchParams.size, Array.fromAsync) plus one agent sheet; `jihad.compat.trace` names the engine call that throws an XPCOM result to a page; the API probe page's second list; registered by the bundle, created by the daemon (daemon 13) | cavekit-modern-web.md | R1, R6 | T-496 | yes | M | DONE (2026-10-16: 16/16 on the shims' test page on the device; Etsy's 47 errors gone and its search styled; DuckDuckGo's results render — its open menu panel is T-499; all three variants carry the component) |
| T-499 | The layout viewport follows a page's overflow (found 2026-10-16: DuckDuckGo's slide-out menu, parked at innerWidth + 4 = 772, is on screen because the page is 1000 wide and the card fit-zooms the whole width): an offscreen engine window as wide as the content once per load, the card's size kept apart from the engine's in the pan/floor arithmetic (`BrowserPageGoanna.cpp` emitGeometry/setScrollPosition, `GoannaRenderPage.cpp`), pref-gated, measured on DuckDuckGo, Wikipedia, BBC, YouTube mobile, with rotation | cavekit-modern-web.md | R6 | T-498 | yes | L | PARTIAL 2026-10-16 (commit `4ded8d72`: `GoannaRenderPage::SetLayoutSize` and the once-per-page rule in `emitGeometry`, pref `jihad.viewport.followOverflow`; open: the engine window resizes but the page still reads innerWidth 768, the paint path restoring the card's size) |
| T-499 | The layout viewport follows a page's overflow (found 2026-10-16: DuckDuckGo's slide-out menu, parked at innerWidth + 4 = 772, is on screen because the page is 1000 wide and the card fit-zooms the whole width): an offscreen engine window as wide as the content once per load, the card's size kept apart from the engine's in the pan/floor arithmetic (`BrowserPageGoanna.cpp` emitGeometry/setScrollPosition, `GoannaRenderPage.cpp`), pref-gated, measured on DuckDuckGo, Wikipedia, BBC, YouTube mobile, with rotation | cavekit-modern-web.md | R6 | T-498 | yes | L | DONE 2026-10-19 (the card's own size resent keeps the layout viewport; the layout viewport is the document's, back to the card's size for the next one, decided once the document is parsed and never on about:blank; probe page with rotation, BBC, YouTube mobile, Wikipedia, DuckDuckGo on the Enyo card) |
| T-500 | The stall (the owner 2026-10-16: "fix the stall"): a leaf-PC stall sampler in the daemon (`$JIHAD_STALL_SAMPLER`, `tools/host/stall-resolve.py` against the unstripped libxul) names what holds the thread — the pages' own JavaScript, compile, the collections; the clock found at 192–384 MHz under load (Palm's ondemandtcl ramps on a touch, up_threshold 95); the clock hold in `JihadBrowserServer.cpp` (the frequency floor at the top while the engine has work, released after 2 s idle; pref `jihad.cpu.hold`); never the governor (it wedged the kernel's cpufreq lock: a reboot) | cavekit-modern-web.md | R6 | T-498 | yes | L | DONE 2026-10-16 (commit `4ded8d72`: the stall sampler and `tools/host/stall-resolve.py`; the clock hold in `JihadBrowserServer.cpp`, pref `jihad.cpu.hold`, never the governor; both in use in the GPU rounds of 2026-10-18) |
| T-501 | H.264 and AAC: FFmpeg 4.4.5's decoders cross-built for the device (`build/webos-oe/fetch-ffmpeg.sh`, `build-ffmpeg-arm.sh`: libavcodec.so.58 + libavutil.so.56, LGPL v2.1+, decoders and parsers only), shipped by the bundle when built, NOTICE and `licenses/runtime/`; the engine's FFmpegRuntimeLinker loads them for `<video>`, `<audio>` and Media Source Extensions | cavekit-gre-widgets.md | R1 | — | yes | M | DONE 2026-10-16, re-verified 2026-10-19 (commit `4ded8d72` built and played them; a device later updated only by `push-engine-update.sh` had lost the libraries and refused every MP4, MP3 and AAC; the push now carries them, and H.264, AAC and MP3 play to the end again on the device) |
| T-484 | Wikipedia's layout decided and recorded; every fix of T-481…T-483 re-tested and its ledger row updated with a screenshot; the README hero retaken on sites that work | cavekit-modern-web.md | R1, R5 | T-481, T-482, T-483 | yes | S | DONE (Wikipedia: desktop kept; the README hero recomposed 14:52 — Internet Archive, BBC News, MDN without adverts, unthemed, Aero backdrop — in `docs/screenshots/cardview.jpg`) |
+1
View File
@@ -80,6 +80,7 @@ modifications; the same changes are the commits of `jihad/uxp-RB_20261006`, one
| `0055-gpu-compositor-a-band-frame-s-regions-composed-in-on.patch` | `gfx/layers/composite/LayerManagerComposite.{h,cpp}`, `gfx/layers/ipc/CompositorBridgeParent.cpp` | T-517: a band frame's bands and column chunks composed as regions of one composite (`JihadSetRegions`: the transaction and BeginFrame/EndFrame once, per region its world, `Prepare` and `RenderLayer`); `JIHAD_GPU_PASS_PER_REGION=1` as before |
| `0056-layers-a-tile-wholly-outside-the-clip-is-not-locked-.patch` | `gfx/layers/composite/TiledContentHost.cpp` | T-517: a tile whose transformed bounds miss the clip (in the render target's coordinates) is skipped before its texture is locked and bound; `JIHAD_GPU_NO_TILE_CULL=1` for A/B |
| `0057-gpu-diagnostics-off-by-default-behind-JIHAD_GPU_LOG-.patch` | `gfx/2d/JihadGpuLog.h` (new, exported), `gfx/2d/moz.build`, the GPU path's files in `gfx/gl`, `gfx/layers`, `layout/base/nsLayoutUtils.cpp`, `widget/PuppetWidget.cpp` | T-512: the `[jihad-gpu]` diagnostics behind one gate (`JIHAD_GPU_LOG=1`, or a device round's `JIHAD_GPU_TIMELINE`/`VERIFY`/`DUMP_EVERY`), failures and fallbacks unconditional; T-517: the page's layer transactions and every compose job's tile uploads on `JIHAD_GPU_TIMELINE` |
| `0058-gpu-compositor-the-review-of-Tier-87-fixed-scratch-p.patch` | `gfx/gl/GLContextProviderEGL.cpp`, `gfx/layers/composite/AsyncCompositionManager.{h,cpp}`, `gfx/layers/ipc/CompositorBridgeParent.cpp`, `gfx/layers/opengl/CompositorOGL.{h,cpp}`, `gfx/layers/opengl/TextureHostOGL.cpp`, `widget/PuppetWidget.cpp` | T-513: the review of Tier 87 (`context/impl/impl-review-findings-gpu-2.md` E1-E7 and Codex's C1): the band scratch is each compositor's own (two cards shared one and overran it); a closed card's frame-sized and band pbuffers and the window targets', band target's and swizzle pass's GL objects are freed (16 MB of GPU memory a card before); a band frame of 257-320 rows stacked in the frame-sized target makes that surface current first; a WebGL canvas's texture name is the producer's to delete; the reads carry their request's cancel flag (`JihadSetCancel`), so a read given up past the daemon's bound stops after `glReadPixels` and never copies into the slot, on the opt-in scratch, swizzle and read-thread paths too, counted by `jihad_offscreen_late_writes()`; a frame whose chunks pass the 12-band cap takes the whole path; a sticky layer's stuck probe reaches twice the composite's scroll step |
| `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 |
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+23
View File
@@ -0,0 +1,23 @@
hook present
--- daemon
[jihad-bs] client connected
[jihad-bs] emitGeometry contentSize=768x942 win=768x942 mZoom=1.0000
[jihad-bs] emitGeometry contentSize=768x8259 win=768x942 mZoom=1.0000
[jihad-bs] emitGeometry contentSize=768x8776 win=768x942 mZoom=1.0000
[jihad-bs] emitGeometry contentSize=1024x6896 win=1024x686 mZoom=1.3333
[jihad-bs] emitGeometry contentSize=1024x6643 win=1024x686 mZoom=1.0000
[jihad-bs] emitGeometry contentSize=768x8776 win=768x942 mZoom=0.7500
[jihad-bs] emitGeometry contentSize=768x8241 win=768x942 mZoom=1.0000
[jihad-bs] emitGeometry contentSize=768x7988 win=768x942 mZoom=1.0000
--- hook
00:39:33 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: start
00:39:42 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: opened: view=browser url=https://www.bbc.com/news engine=google defaultSearch=google home=https://lite.duckduckgo.com/lite/ bookmarks=2 history=211 toast=[]
00:39:52 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation left asked
00:39:55 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation left: window 1024x740
00:40:09 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation up asked
00:40:12 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation up: window 768x996
00:40:26 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation free asked
00:40:29 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation free: window 768x996
-rw-r--r-- 1 root root 148023 Oct 19 17:39 /tmp/t499-bbc-a.png
-rw-r--r-- 1 root root 67250 Oct 19 17:40 /tmp/t499-bbc-b.png
-rw-r--r-- 1 root root 147898 Oct 19 17:40 /tmp/t499-bbc-c.png
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

+20
View File
@@ -0,0 +1,20 @@
hook present
--- daemon
[jihad-bs] client connected
[jihad-bs] emitGeometry contentSize=768x942 win=768x942 mZoom=1.0000
[jihad-bs] emitGeometry contentSize=980x1202 win=768x942 mZoom=1.0000
[jihad-bs] emitGeometry contentSize=768x942 win=768x942 mZoom=0.7837
[jihad-bs] emitGeometry contentSize=1000x943 win=768x942 mZoom=1.0000
[jihad-bs] emitGeometry contentSize=1024x3018 win=1024x686 mZoom=1.0240
[jihad-bs] emitGeometry contentSize=1000x3015 win=768x942 mZoom=0.7500
--- hook
01:20:34 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: opened: view=browser url=https://duckduckgo.com/?q=webOS&ia=web engine=google defaultSearch=google home=https://lite.duckduckgo.com/lite/ bookmarks=2 history=215 toast=[]
01:20:44 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation left asked
01:20:47 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation left: window 1024x740
01:21:01 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation up asked
01:21:04 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation up: window 768x996
01:21:18 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation free asked
01:21:21 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation free: window 768x996
-rw-r--r-- 1 root root 58216 Oct 19 18:20 /tmp/t499-ddg-a.png
-rw-r--r-- 1 root root 27523 Oct 19 18:20 /tmp/t499-ddg-b.png
-rw-r--r-- 1 root root 274192 Oct 19 18:21 /tmp/t499-ddg-c.png
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

+23
View File
@@ -0,0 +1,23 @@
hook present
--- daemon
[jihad-bs] client connected
[jihad-bs] emitGeometry contentSize=768x942 win=768x942 mZoom=1.0000
[jihad-bs] emitGeometry contentSize=1000x1418 win=768x942 mZoom=1.0000
[jihad-bs] layout viewport 1000x1226 (card 768x942)
[jihad-bs] title changed by the page: [iw=1000 cw=996 sw=1000 parked.x=1004]
[jihad-bs] emitGeometry contentSize=1024x1418 win=1024x686 mZoom=1.0240
[jihad-bs] title changed by the page: [iw=1024 cw=1020 sw=1024 parked.x=1028]
[jihad-bs] layout viewport 1000x893 (card 768x686)
[jihad-bs] emitGeometry contentSize=1000x1418 win=768x686 mZoom=0.7500
[jihad-bs] layout viewport 1000x1226 (card 768x942)
[jihad-bs] title changed by the page: [iw=1000 cw=996 sw=1000 parked.x=1004]
--- hook
11:21:27 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation left asked
11:21:30 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation left: window 1024x740
11:21:44 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation up asked
11:21:47 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation up: window 768x996
11:22:01 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation free asked
11:22:04 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation free: window 768x996
-rw-r--r-- 1 root root 63963 Oct 24 04:21 /tmp/t499-probe-a.png
-rw-r--r-- 1 root root 31812 Oct 24 04:21 /tmp/t499-probe-b.png
-rw-r--r-- 1 root root 63963 Oct 24 04:21 /tmp/t499-probe-c.png
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

+17
View File
@@ -0,0 +1,17 @@
hook present
--- daemon
[jihad-bs] client connected
[jihad-bs] emitGeometry contentSize=768x942 win=768x942 mZoom=1.0000
[jihad-bs] emitGeometry contentSize=1116x11793 win=768x942 mZoom=1.0000
[jihad-bs] emitGeometry contentSize=1116x16380 win=768x942 mZoom=0.6882
[jihad-bs] emitGeometry contentSize=1024x11092 win=1024x686 mZoom=0.9176
[jihad-bs] emitGeometry contentSize=1024x10587 win=1024x686 mZoom=1.0000
[jihad-bs] emitGeometry contentSize=768x13212 win=768x942 mZoom=0.7500
--- hook
01:18:25 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation up asked
01:18:28 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation up: window 768x996
01:18:42 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation free asked
01:18:45 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation free: window 768x996
-rw-r--r-- 1 root root 53791 Oct 19 18:18 /tmp/t499-wiki-a.png
-rw-r--r-- 1 root root 249183 Oct 19 18:18 /tmp/t499-wiki-b.png
-rw-r--r-- 1 root root 357842 Oct 19 18:18 /tmp/t499-wiki-c.png
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

+20
View File
@@ -0,0 +1,20 @@
hook present
--- daemon
[jihad-bs] client connected
[jihad-bs] emitGeometry contentSize=768x942 win=768x942 mZoom=1.0000
[jihad-bs] emitGeometry contentSize=768x2420 win=768x942 mZoom=1.0000
[jihad-bs] emitGeometry contentSize=1024x1695 win=1024x686 mZoom=1.3333
[jihad-bs] emitGeometry contentSize=1024x686 win=1024x686 mZoom=1.0000
[jihad-bs] emitGeometry contentSize=768x942 win=768x942 mZoom=0.7500
--- hook
01:19:00 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: start
01:19:08 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: opened: view=browser url=https://m.youtube.com/ engine=google defaultSearch=google home=https://lite.duckduckgo.com/lite/ bookmarks=2 history=214 toast=[]
01:19:18 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation left asked
01:19:21 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation left: window 1024x740
01:19:35 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation up asked
01:19:38 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation up: window 768x996
01:19:52 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation free asked
01:19:55 JIHAD-TEST] page:w10;Oleft;w12;Oup;w12;Ofree: orientation free: window 768x996
-rw-r--r-- 1 root root 51639 Oct 19 18:19 /tmp/t499-ytm-a.png
-rw-r--r-- 1 root root 50691 Oct 19 18:19 /tmp/t499-ytm-b.png
-rw-r--r-- 1 root root 87387 Oct 19 18:19 /tmp/t499-ytm-c.png
Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

+48
View File
@@ -0,0 +1,48 @@
[jihad-bs] inject pan 0 -> 6000 over 700 ms
[jihad-gpu] verify slot 2 (y -141, shift 0, bands 0..0 0..0): 1083 rows differ in 1 runs: 141..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 0 (y 5387, shift 891, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 5859, shift 472, bands 385..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 5859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 5859, shift 0, bands 0..0 0..0): 1224 rows differ in 1 runs: 0..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan 6000 -> 0 over 700 ms
[jihad-gpu] verify slot 1 (y 5859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 2 (y -141, shift -574, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y -141, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan 0 -> 6000 over 700 ms
[jihad-gpu] verify slot 1 (y -141, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y -64, shift 77, bands 1147..1224 0..0): 106 rows differ in 18 runs: 185..192 205..206 208..209 211..212 214..215 232..233 235..236 238..239 ...; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 3381, shift 865, bands 359..1224 0..0): 69 rows differ in 7 runs: 216..217 219..220 249..250 265..282 287..304 319..335 341..357; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 0 (y 5379, shift 703, bands 521..1224 0..0): 32 rows differ in 6 runs: 195..196 465..474 475..476 483..497 498..499 515..521; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 5859, shift 480, bands 587..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 5859, shift 0, bands 0..0 0..0): 1224 rows differ in 1 runs: 0..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan 6000 -> 0 over 700 ms
[jihad-gpu] verify slot 0 (y 5859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 5190, shift -669, bands 0..669 0..0): 106 rows differ in 7 runs: 1055..1060 1078..1094 1100..1117 1131..1148 1153..1170 1185..1202 1207..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 4573, shift -617, bands 0..617 0..0): 96 rows differ in 10 runs: 840..841 1001..1002 1067..1069 1075..1088 1089..1091 1106..1123 1128..1145 1160..1176 ...; 0 rows wrong:; 0 rows structurally
[jihad-gpu] verify slot 0 (y 4573, shift 0, bands 0..263 0..0): 96 rows differ in 10 runs: 840..841 1001..1002 1067..1069 1075..1088 1089..1091 1106..1123 1128..1145 1160..1176 ...; 0 rows wrong:; 0 rows structurally wro
[jihad-bs] inject pan done
[jihad-gpu] verify slot 0 (y -141, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y -141, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y -141, shift 0, bands 0..0 0..0): 1083 rows differ in 1 runs: 141..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan 0 -> 6000 over 700 ms
[jihad-gpu] verify slot 0 (y -141, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 2241, shift 737, bands 487..1224 0..0): 65 rows differ in 8 runs: 388..402 404..405 410..411 412..427 437..438 442..458 464..480 481..482; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 0 (y 3261, shift 1020, bands 204..1224 0..0): 10 rows differ in 3 runs: 186..187 194..196 197..204; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 5859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 5859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 5859, shift 0, bands 0..0 0..0): 1224 rows differ in 1 runs: 0..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan 6000 -> 0 over 700 ms
[jihad-gpu] verify slot 2 (y 5859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 3870, shift -651, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 1 (y -141, shift -120, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y -141, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y -141, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y -141, shift 0, bands 141..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
ls: /tmp/vw-*.raw: No such file or directory
ls: /tmp/vs-*.raw: No such file or directory
33 verify lines, 0 rows wrong, 0 rows structurally wrong
+55
View File
@@ -0,0 +1,55 @@
[jihad-bs] inject pan 0 -> 6000 over 700 ms
[jihad-gpu] verify slot 0 (y -141, shift 0, bands 141..1083 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y -64, shift 77, bands 1147..1224 0..0): 1027 rows differ in 5 runs: 141..1083 1094..1114 1121..1142 1161..1183 1189..1211; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 1 (y 5859, shift 1115, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 5859, shift 0, bands 374..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 5859, shift 0, bands 0..0 0..0): 107 rows differ in 10 runs: 0..20 27..29 1055..1056 1059..1061 1064..1066 1092..1107 1114..1135 1154..1175 ...; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 5859, shift 0, bands 0..0 0..0): 107 rows differ in 10 runs: 0..20 27..29 1055..1056 1059..1061 1064..1066 1092..1107 1114..1135 1154..1175 ...; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan 6000 -> 0 over 700 ms
[jihad-gpu] verify slot 2 (y 5859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 3570, shift -711, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 2447, shift -1123, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 0 (y 1873, shift -574, bands 0..574 0..0): 445 rows differ in 3 runs: 771..810 817..838 839..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 1873, shift 0, bands 0..273 0..0): 445 rows differ in 3 runs: 771..810 817..838 839..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y -141, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y -141, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan 0 -> 6000 over 700 ms
[jihad-gpu] verify slot 0 (y -141, shift 0, bands 0..0 0..0): 41 rows differ in 2 runs: 1171..1191 1198..1219; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 2113, shift 549, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 3056, shift 943, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 1 (y 3647, shift 591, bands 633..1224 0..0): 153 rows differ in 1 runs: 141..294; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 5859, shift 0, bands 0..0 0..0): 107 rows differ in 10 runs: 0..20 27..29 1055..1056 1059..1061 1064..1066 1092..1107 1114..1135 1154..1175 ...; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 5859, shift 0, bands 0..0 0..0): 107 rows differ in 10 runs: 0..20 27..29 1055..1056 1059..1061 1064..1066 1092..1107 1114..1135 1154..1175 ...; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan 6000 -> 0 over 700 ms
[jihad-gpu] verify slot 0 (y 5859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 3870, shift -454, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 3141, shift -729, bands 0..1091 0..0): 133 rows differ in 1 runs: 1091..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 2824, shift -317, bands 0..317 0..0): 710 rows differ in 1 runs: 514..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 0 (y -141, shift -368, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y -141, shift 0, bands 141..167 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y -141, shift 0, bands 0..0 0..0): 41 rows differ in 2 runs: 1171..1191 1198..1219; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan 0 -> 6000 over 700 ms
[jihad-gpu] verify slot 0 (y -141, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 2456, shift 1200, bands 24..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 2953, shift 497, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 1 (y 3364, shift 411, bands 813..1224 0..0): 497 rows differ in 1 runs: 316..813; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 3364, shift 0, bands 289..316 0..0): 497 rows differ in 1 runs: 316..813; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 5859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 5859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan 6000 -> 0 over 700 ms
[jihad-gpu] verify slot 1 (y 5859, shift 0, bands 0..0 0..0): 107 rows differ in 10 runs: 0..20 27..29 1055..1056 1059..1061 1064..1066 1092..1107 1114..1135 1154..1175 ...; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 3613, shift -626, bands 0..626 0..0): 373 rows differ in 4 runs: 823..906 913..934 953..974 976..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 2859, shift -754, bands 0..1112 0..0): 112 rows differ in 1 runs: 1112..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 2 (y 2259, shift -600, bands 0..600 0..0): 373 rows differ in 2 runs: 797..830 884..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 2259, shift 0, bands 0..292 0..0): 373 rows differ in 2 runs: 797..830 884..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y -141, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y -141, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
ls: /tmp/vw-*.raw: No such file or directory
ls: /tmp/vs-*.raw: No such file or directory
40 verify lines, 0 rows wrong, 0 rows structurally wrong
+55
View File
@@ -0,0 +1,55 @@
[jihad-bs] inject pan 0 -> 6000 over 700 ms
[jihad-gpu] verify slot 1 (y -133, shift 8, bands 1216..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 13, shift 146, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 313, shift 300, bands 924..1224 0..0): 736 rows differ in 3 runs: 164..738 745..766 783..924; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 2 (y 5859, shift 660, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 5859, shift 0, bands 396..436 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 5859, shift 0, bands 0..0 0..0): 107 rows differ in 10 runs: 0..20 27..29 1055..1056 1059..1061 1064..1066 1092..1107 1114..1135 1154..1175 ...; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan 6000 -> 0 over 700 ms
[jihad-gpu] verify slot 2 (y 5859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 3450, shift -814, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 2284, shift -1166, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 0 (y 1624, shift -660, bands 0..660 0..0): 348 rows differ in 2 runs: 857..883 902..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 1624, shift 0, bands 0..265 0..0): 348 rows differ in 2 runs: 857..883 902..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y -141, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y -141, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan 0 -> 6000 over 700 ms
[jihad-gpu] verify slot 0 (y -141, shift 0, bands 0..0 0..0): 41 rows differ in 2 runs: 1171..1191 1198..1219; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 2876, shift 532, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 3776, shift 900, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 1 (y 3939, shift 163, bands 1061..1224 0..0): 720 rows differ in 1 runs: 341..1061; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 5859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 5859, shift 0, bands 0..0 0..0): 107 rows differ in 10 runs: 0..20 27..29 1055..1056 1059..1061 1064..1066 1092..1107 1114..1135 1154..1175 ...; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan 6000 -> 0 over 700 ms
[jihad-gpu] verify slot 0 (y 5859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 3561, shift -678, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 2979, shift -582, bands 0..940 0..0): 284 rows differ in 1 runs: 940..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 0 (y -141, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y -141, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y -141, shift 0, bands 0..0 0..0): 41 rows differ in 2 runs: 1171..1191 1198..1219; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan 0 -> 6000 over 700 ms
[jihad-gpu] verify slot 0 (y -141, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 1993, shift 600, bands 624..1224 0..0): 373 rows differ in 1 runs: 251..624; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 2807, shift 814, bands 410..1224 0..0): 102 rows differ in 1 runs: 141..243; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 2807, shift 0, bands 243..1224 0..0): 102 rows differ in 1 runs: 141..243; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 1 (y 5859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 5859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 5859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan 6000 -> 0 over 700 ms
[jihad-gpu] verify slot 0 (y 5601, shift -258, bands 0..258 0..0): 769 rows differ in 1 runs: 455..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 5319, shift -282, bands 0..282 415..718): 379 rows differ in 4 runs: 718..792 805..825 831..860 968..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 0 (y 356, shift -960, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y -141, shift -497, bands 0..909 0..0): 315 rows differ in 1 runs: 909..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y -141, shift 0, bands 0..0 0..0): 315 rows differ in 1 runs: 909..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y -141, shift 0, bands 0..0 0..0): 315 rows differ in 1 runs: 909..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y -141, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y -141, shift 0, bands 197..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
ls: /tmp/vw-*.raw: No such file or directory
ls: /tmp/vs-*.raw: No such file or directory
40 verify lines, 0 rows wrong, 0 rows structurally wrong
@@ -0,0 +1,35 @@
=== card 1, a fast flick (pan 0 8000 800)
[jihad-bs] inject pan 0 -> 8000 over 800 ms
[jihad-gpu] verify slot 2 (y -141, shift 0, bands 141..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y -51, shift 90, bands 1134..1224 0..0): 109 rows differ in 17 runs: 192..193 195..196 198..199 201..202 219..220 222..223 225..226 228..229 ...; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify2 (whole at -141 shifted 90 vs whole at -51): 147 rows differ in 16 runs: 51..95 116..128 141..185 192..193 195..196 198..199 201..202 219..220 ...
[jihad-gpu] verify slot 1 (y 329, shift 380, bands 844..1224 0..0): 1180 rows differ in 2 runs: 0..141 185..1224; 744 rows wrong: 0..16 22..38 53..69 75..91 107..123 129..141 185..198 209..253 ...; 736 rows structurally wrong: 0..
[jihad-gpu] verify2 (whole at -51 shifted 380 vs whole at 329): 44 rows differ in 1 runs: 141..185
[jihad-bs] inject pan done
[jihad-gpu] verify slot 1 (y 7859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 7859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 7858, shift -1, bands 0..1 0..0): 98 rows differ in 10 runs: 0..1 15..32 37..54 69..85 91..107 122..139 185..187 188..189 ...; 1 rows wrong: 185..186; 0 rows structurally wrong:
=== card 2 (file:///tmp/jihad-sticky-mix.html), a fast flick
[jihad-bs] inject: commands go to the newest card
[jihad-bs] inject pan 0 -> 8000 over 800 ms
[jihad-gpu] verify slot 1 (y -141, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 2429, shift 1130, bands 94..1224 0..0): 861 rows differ in 3 runs: 0..141 156..164 187..899; 537 rows wrong: 2..12 13..16 33..51 59..77 88..98 99..102 119..137 199..275 ...; 544 rows structurally wrong
[jihad-gpu] verify2 (whole at 1299 shifted 1130 vs whole at 2429): 0 rows differ in 0 runs:
[jihad-gpu] verify slot 1 (y 3179, shift 750, bands 0..1224 0..0): 257 rows differ in 8 runs: 0..141 161..164 200..210 239..256 298..333 403..427 597..614 792..802; 102 rows wrong: 0..11 23..33 34..37 48..124 139..141; 96 rows str
[jihad-gpu] verify2 (whole at 2429 shifted 750 vs whole at 3179): 464 rows differ in 2 runs: 0..269 279..474
[jihad-bs] inject pan done
[jihad-gpu] verify slot 2 (y 3829, shift 650, bands 574..1224 0..0): 1184 rows differ in 2 runs: 0..1043 1083..1224; 715 rows wrong: 0..73 89..107 114..132 189..192 199..217 229..243 260..278 285..303 ...; 776 rows structurally wr
[jihad-gpu] verify2 (whole at 3179 shifted 650 vs whole at 3829): 572 rows differ in 2 runs: 0..378 380..574
[jihad-gpu] verify slot 2 (y 7859, shift 0, bands 0..0 0..0): 134 rows differ in 11 runs: 0..15 17..18 21..40 47..66 1109..1127 1133..1152 1156..1157 1158..1160 ...; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 7859, shift 0, bands 0..0 0..0): 134 rows differ in 11 runs: 0..15 17..18 21..40 47..66 1109..1127 1133..1152 1156..1157 1158..1160 ...; 0 rows wrong:; 0 rows structurally wrong:
=== card 1 again (target focus), a fast flick
[jihad-bs] inject: commands go to the focused card
[jihad-bs] inject pan 0 -> 8000 over 800 ms
[jihad-gpu] verify slot 1 (y -141, shift 0, bands 0..0 0..0): 1083 rows differ in 1 runs: 141..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 0 (y 7859, shift 460, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify2 (whole at 7399 shifted 460 vs whole at 7859): 197 rows differ in 34 runs: 15..17 18..23 24..29 30..31 36..38 39..41 42..44 45..47 ...
[jihad-gpu] verify slot 1 (y 7859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 7858, shift -1, bands 0..1 0..0): 98 rows differ in 10 runs: 0..1 15..32 37..54 69..85 91..107 122..139 185..187 188..189 ...; 1 rows wrong: 185..186; 0 rows structurally wrong:
=== totals
22 verify lines, 2849 rows wrong, 3168 rows structurally wrong
@@ -0,0 +1,31 @@
=== card 1, a fast flick (pan 0 8000 800)
[jihad-bs] inject pan 0 -> 8000 over 800 ms
[jihad-gpu] verify slot 2 (y -141, shift 0, bands 0..0 0..0): 1083 rows differ in 1 runs: 141..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 0 (y 7858, shift -1, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 7858, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
=== card 2 (file:///tmp/jihad-sticky-mix.html), a fast flick
[jihad-bs] inject: commands go to the newest card
[jihad-bs] inject pan 0 -> 8000 over 800 ms
[jihad-gpu] verify slot 1 (y -141, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 2399, shift 1100, bands 124..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 4089, shift 340, bands 884..1224 0..0): 359 rows differ in 1 runs: 344..703; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 1 (y 7859, shift 890, bands 334..1224 0..0): 12 rows differ in 1 runs: 141..153; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 7859, shift 0, bands 478..1224 0..0): 12 rows differ in 1 runs: 141..153; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 7859, shift 0, bands 0..0 0..0): 146 rows differ in 12 runs: 0..15 17..18 21..40 47..66 141..153 1109..1127 1133..1152 1156..1157 ...; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 7859, shift 0, bands 0..0 0..0): 146 rows differ in 12 runs: 0..15 17..18 21..40 47..66 141..153 1109..1127 1133..1152 1156..1157 ...; 0 rows wrong:; 0 rows structurally wrong:
=== card 1 again (target focus), a fast flick
[jihad-bs] inject: commands go to the focused card
[jihad-bs] inject pan 0 -> 8000 over 800 ms
[jihad-gpu] verify slot 1 (y -141, shift 0, bands 0..0 0..0): 1083 rows differ in 1 runs: 141..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 2629, shift 1060, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 2 (y 7859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 7859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 7858, shift -1, bands 0..1 0..0): 1222 rows differ in 2 runs: 1..185 186..1224; 622 rows wrong: 15..18 19..28 29..31 37..38 39..40 41..46 47..50 51..52 ...; 16 rows structurally wrong: 592..608
=== totals
22 verify lines, 622 rows wrong, 16 rows structurally wrong
00:59:24 JIHAD-TEST] page:w28;A: start
00:59:32 JIHAD-TEST] page:w28;A: opened: view=browser url=file:///tmp/jihad-sticky-mix.html engine=google defaultSearch=google home=https://lite.duckduckgo.com/lite/ bookmarks=2 history=213 toast=[]
01:00:00 JIHAD-TEST] page:w28;A: activated the other card
@@ -0,0 +1,30 @@
=== card 1, a fast flick (pan 0 8000 800)
[jihad-bs] inject pan 0 -> 8000 over 800 ms
[jihad-gpu] verify slot 2 (y -141, shift 0, bands 0..0 0..0): 1083 rows differ in 1 runs: 141..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 2419, shift 1150, bands 74..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 1 (y 7859, shift 520, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 7859, shift 0, bands 1205..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 7858, shift -1, bands 0..1 0..0): 1222 rows differ in 2 runs: 1..185 186..1224; 622 rows wrong: 15..18 19..28 29..31 37..38 39..40 41..46 47..50 51..52 ...; 16 rows structurally wrong: 592..608
=== card 2 (file:///tmp/jihad-sticky-mix.html), a fast flick
[jihad-bs] inject: commands go to the newest card
[jihad-bs] inject pan 0 -> 8000 over 800 ms
[jihad-gpu] verify slot 1 (y -141, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 2639, shift 1220, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 0 (y 7859, shift 440, bands 784..1224 0..0): 204 rows differ in 6 runs: 141..188 189..238 253..272 277..298 303..322 378..427; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 7859, shift 0, bands 499..528 0..0): 204 rows differ in 6 runs: 141..188 189..238 253..272 277..298 303..322 378..427; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 7859, shift 0, bands 0..0 0..0): 338 rows differ in 17 runs: 0..15 17..18 21..40 47..66 141..188 189..238 253..272 277..298 ...; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 7859, shift 0, bands 0..0 0..0): 338 rows differ in 17 runs: 0..15 17..18 21..40 47..66 141..188 189..238 253..272 277..298 ...; 0 rows wrong:; 0 rows structurally wrong:
=== card 1 again (target focus), a fast flick
[jihad-bs] inject: commands go to the focused card
[jihad-bs] inject pan 0 -> 8000 over 800 ms
[jihad-gpu] verify slot 1 (y -141, shift 0, bands 0..0 0..0): 1083 rows differ in 1 runs: 141..1224; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 2899, shift 1140, bands 84..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 1 (y 7859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 7859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 7858, shift -1, bands 0..1 0..0): 1222 rows differ in 2 runs: 1..185 186..1224; 622 rows wrong: 15..18 19..28 29..31 37..38 39..40 41..46 47..50 51..52 ...; 16 rows structurally wrong: 592..608
=== totals
23 verify lines, 1244 rows wrong, 32 rows structurally wrong
01:07:20 JIHAD-TEST] page:w28;A: activated the other card
@@ -0,0 +1,33 @@
=== card 1, a fast flick (pan 0 8000 800)
[jihad-bs] inject pan 0 -> 8000 over 800 ms
[jihad-gpu] verify slot 0 (y -141, shift 0, bands 141..1083 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y -51, shift 90, bands 1134..1224 0..0): 491 rows differ in 11 runs: 141..149 176..286 293..297 298..306 409..499 643..733 993..1101 1108..1129 ...; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 139, shift 190, bands 1034..1224 0..0): 807 rows differ in 2 runs: 161..737 803..1034; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 139, shift 0, bands 141..161 0..0): 807 rows differ in 2 runs: 161..737 803..1034; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 0 (y 7859, shift 80, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 7859, shift 0, bands 510..531 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 7859, shift 0, bands 0..0 0..0): 113 rows differ in 7 runs: 0..20 39..60 67..77 1129..1147 1154..1175 1194..1215 1222..1224; 0 rows wrong:; 0 rows structurally wrong:
=== card 2 (file:///tmp/jihad-sticky-mix.html), a fast flick
[jihad-bs] inject: commands go to the newest card
[jihad-bs] inject pan 0 -> 8000 over 800 ms
[jihad-gpu] verify slot 1 (y -141, shift 0, bands 0..0 0..0): 46 rows differ in 4 runs: 1165..1166 1167..1191 1196..1197 1198..1218; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 3029, shift 1130, bands 94..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 0 (y 7859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 7859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 7859, shift 0, bands 0..0 0..0): 134 rows differ in 11 runs: 0..15 17..18 21..40 47..66 1109..1127 1133..1152 1156..1157 1158..1160 ...; 0 rows wrong:; 0 rows structurally wrong:
=== card 1 again (target focus), a fast flick
[jihad-bs] inject: commands go to the focused card
[jihad-bs] inject pan 0 -> 8000 over 800 ms
[jihad-gpu] verify slot 1 (y -141, shift 0, bands 0..0 0..0): 41 rows differ in 2 runs: 1171..1191 1198..1219; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 1729, shift 450, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 2499, shift 770, bands 0..238 454..1224): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 2889, shift 390, bands 834..1224 0..0): 82 rows differ in 4 runs: 141..158 183..193 197..202 209..259; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 0 (y 7859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 7859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 7859, shift 0, bands 0..0 0..0): 113 rows differ in 7 runs: 0..20 39..60 67..77 1129..1147 1154..1175 1194..1215 1222..1224; 0 rows wrong:; 0 rows structurally wrong:
=== totals
26 verify lines, 0 rows wrong, 0 rows structurally wrong
01:14:15 JIHAD-TEST] page:w28;A: activated the other card
@@ -0,0 +1,32 @@
=== card 1, a fast flick (pan 0 8000 800)
[jihad-bs] inject pan 0 -> 8000 over 800 ms
[jihad-gpu] verify slot 2 (y -141, shift 0, bands 141..1083 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y -61, shift 80, bands 1144..1224 0..0): 446 rows differ in 12 runs: 141..149 186..266 275..296 303..307 308..316 419..499 653..733 1003..1083 ...; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 5389, shift 840, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 6599, shift 1210, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 2 (y 6959, shift 360, bands 864..1224 0..0): 385 rows differ in 1 runs: 479..864; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 7859, shift 900, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 7859, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 7859, shift 0, bands 0..0 0..0): 113 rows differ in 7 runs: 0..20 39..60 67..77 1129..1147 1154..1175 1194..1215 1222..1224; 0 rows wrong:; 0 rows structurally wrong:
=== card 2 (file:///tmp/jihad-sticky-mix.html), a fast flick
[jihad-bs] inject: commands go to the newest card
[jihad-bs] inject pan 0 -> 8000 over 800 ms
[jihad-gpu] verify slot 1 (y -141, shift 0, bands 141..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 29, shift 170, bands 1054..1224 0..0): 558 rows differ in 12 runs: 141..266 329..499 563..733 747..766 767..768 773..774 775..791 801..802 ...; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 0 (y 429, shift 400, bands 824..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 0 (y 7859, shift 590, bands 0..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 1 (y 7859, shift 0, bands 488..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-gpu] verify slot 2 (y 7859, shift 0, bands 0..0 0..0): 134 rows differ in 11 runs: 0..15 17..18 21..40 47..66 1109..1127 1133..1152 1156..1157 1158..1160 ...; 0 rows wrong:; 0 rows structurally wrong:
=== card 1 again (target focus), a fast flick
[jihad-bs] inject: commands go to the focused card
[jihad-bs] inject pan 0 -> 8000 over 800 ms
[jihad-gpu] verify slot 1 (y -141, shift 0, bands 0..0 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
[jihad-bs] inject pan done
[jihad-gpu] verify slot 1 (y 7859, shift 400, bands 824..1224 0..0): 0 rows differ in 0 runs:; 0 rows wrong:; 0 rows structurally wrong:
=== totals
22 verify lines, 0 rows wrong, 0 rows structurally wrong
11:17:39 JIHAD-TEST] page:w28;A: start
11:17:47 JIHAD-TEST] page:w28;A: opened: view=browser url=file:///tmp/jihad-sticky-mix.html engine=google defaultSearch=google home=https://lite.duckduckgo.com/lite/ bookmarks=2 history=216 toast=[]
11:18:15 JIHAD-TEST] page:w28;A: activated the other card
+9 -3
View File
@@ -26,6 +26,7 @@ LICENSE@@@ */
#include <sys/ioctl.h>
#include <fcntl.h>
#include <string.h>
#include <stdint.h> // T-513: uint64_t (windowBytes)
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
@@ -188,10 +189,15 @@ size_t BrowserOffscreen::windowBytes() const
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)(rows + m_header->renderedHeight) * rowBytes > (size_t)rasterSize())
// T-513 (review): no product that can wrap: on the device size_t is 32 bits, and even in 64 a
// row count, a height and a width from the header can multiply past it (Codex 2026-10-24). The
// rows the window and the frame take are held against what the raster has room for, by division
// (LunaSysMgr's only guard against blitting outside the segment)
const uint64_t rowBytes = (uint64_t)m_header->renderedWidth * 4;
const uint64_t roomRows = (uint64_t)rasterSize() / rowBytes;
if ((uint64_t)rows + (uint64_t)m_header->renderedHeight > roomRows)
return 0;
return (size_t)rows * rowBytes;
return (size_t)((uint64_t)rows * rowBytes); // <= rasterSize()
}
void BrowserOffscreen::resetBuffer()
+44 -9
View File
@@ -138,6 +138,10 @@ JihadBrowserServer::JihadBrowserServer(const char* name, jihad::EngineHost& host
}
JihadBrowserServer::~JihadBrowserServer() {
#ifndef JIHAD_NO_INJECT
mInjectStop = true; // T-513 (review): the inject threads hold `this`
for (InjectThread& t : mInjectThreads) if (t.thread.joinable()) t.thread.join();
#endif
if (jihad::jihadExpressOn()) jihad::jihadExpressStop(); // T-515: before the lock (it waits for the thread's step)
jihad::FrameLockGuard frameLock; // T-515
if (sExpressServer == this) sExpressServer = nullptr;
@@ -299,6 +303,8 @@ void JihadBrowserServer::clientDisconnected(YapProxy* proxy) {
// drag X Y DX DY -> dragStart/dragProcess/dragEnd (flick scroll path)
// size W H -> setWindowSize (VKB-resize / rotation simulation)
// zoom Z X Y -> setZoomAndScroll
// target focus|last -> this batch and later ones go to the card that last said it has the
// focus (two cards driven in turn), or back to the newest connection
//
// HOW TO TURN IT ON (T-057): set $JIHAD_INJECT in the daemon's environment —
// in the variant's upstart job, or on an ad-hoc novacom run:
@@ -324,6 +330,20 @@ void JihadBrowserServer::clientDisconnected(YapProxy* proxy) {
// group/world-writable. A rejected file is still unlinked so a hostile one
// cannot wedge the channel by sitting there.
// T-513: an inject pan's or zoom's thread. The finished ones are joined first (Codex 2026-10-24: kept
// until shutdown, a long round's threads piled up); the rest at shutdown.
void JihadBrowserServer::startInjectThread(std::function<void()> body) {
for (size_t i = 0; i < mInjectThreads.size();) {
if (mInjectThreads[i].done->load()) { mInjectThreads[i].thread.join(); mInjectThreads.erase(mInjectThreads.begin() + i); }
else ++i;
}
std::shared_ptr<std::atomic<bool>> done = std::make_shared<std::atomic<bool>>(false);
InjectThread t;
t.done = done;
t.thread = std::thread([body, done]() { body(); done->store(true); });
mInjectThreads.push_back(std::move(t));
}
void JihadBrowserServer::processInjectFile() {
const char* path = mInjectPath.c_str();
// O_NOFOLLOW: a symlink at the path is not the file we agreed on. O_NONBLOCK: a
@@ -353,7 +373,14 @@ void JihadBrowserServer::processInjectFile() {
while (fgets(line, sizeof line, f)) cmds.push_back(line);
fclose(f);
unlink(path);
jihad::BrowserPageGoanna* p = pageFor(mLastProxy);
// T-513 rounds: the newest connection is the card a self-drive round opened, but a second card
// panned after the first needs the first back: "target focus" follows the card on screen
for (auto& c : cmds) {
if (c.compare(0, 12, "target focus") == 0) { mInjectFocus = true; printf("[jihad-bs] inject: commands go to the focused card\n"); }
else if (c.compare(0, 11, "target last") == 0) { mInjectFocus = false; printf("[jihad-bs] inject: commands go to the newest card\n"); }
}
jihad::BrowserPageGoanna* p = mInjectFocus ? pageFor(mFocusProxy) : nullptr;
if (!p) p = pageFor(mLastProxy);
if (!p) { printf("[jihad-bs] inject: no page\n"); return; }
for (auto& c : cmds) {
int x=0, y=0, n=1, a=0, b=0;
@@ -388,23 +415,28 @@ void JihadBrowserServer::processInjectFile() {
// drag, every 16 ms, through the proxy's reader side (the express hook, then the main
// loop's queue), so the pan goes on while the main thread is busy, as a finger's does.
const int y0 = x, y1 = y, ms = n > 0 ? n : 1;
std::thread([this, panProxy, y0, y1, ms, scrollPacket]() {
// T-513 (review): the card is the proxy AND its page (a closed card's proxy address can be a
// new card's), and the thread is joined at shutdown
jihad::BrowserPageGoanna* panPage = p;
startInjectThread([this, panProxy, panPage, y0, y1, ms, scrollPacket]() {
const int64_t t0 = jihadTickNowMs();
std::vector<uint8_t> buf;
for (;;) {
if (mInjectStop) return;
const int64_t el = jihadTickNowMs() - t0;
const int py = el >= ms ? y1 : (int)(y0 + (y1 - y0) * (double)el / ms);
const int len = scrollPacket(buf, py);
{
jihad::FrameLockGuard frameLock;
if (mPages.find(panProxy) == mPages.end()) break;
auto it = mPages.find(panProxy);
if (it == mPages.end() || it->second.page != panPage) break;
panProxy->injectAsync(buf.data(), len);
}
if (el >= ms) break;
usleep(16000);
}
printf("[jihad-bs] inject pan done (the card's path)\n");
}).detach();
});
} else {
mPanPage = p; mPanY0 = x; mPanY1 = y; mPanMs = n > 0 ? n : 1; mPanT0 = jihadTickNowMs(); mPanActive = true;
}
@@ -620,20 +652,23 @@ void JihadBrowserServer::processInjectFile() {
bool sent = false;
if (jihad::jihadExpressOn() && zp) {
printf("[jihad-bs] inject zoom %.4f at %d,%d in %d ms (the card's path)\n", z, x, y, delayMs);
std::thread([this, zp, z, x, y, delayMs]() {
if (delayMs > 0) usleep((useconds_t)delayMs * 1000);
jihad::BrowserPageGoanna* zPage = p; // T-513 (review): as the pan's thread
startInjectThread([this, zp, zPage, z, x, y, delayMs]() {
for (int waited = 0; waited < delayMs && !mInjectStop; waited += 20) usleep(20000);
if (mInjectStop) return;
std::vector<uint8_t> buf(kMaxMsgLen, 0);
YapPacket* w = YapProxy::wrapPacket(buf.data(), 0);
(*w) << (int16_t)0x150e; (*w) << z; (*w) << (int32_t)x; (*w) << (int32_t)y;
const int len = w->length();
YapProxy::freePacket(w);
jihad::FrameLockGuard frameLock;
if (mPages.find(zp) != mPages.end()) zp->injectAsync(buf.data(), len);
}).detach();
auto it = mPages.find(zp);
if (it != mPages.end() && it->second.page == zPage) zp->injectAsync(buf.data(), len);
});
sent = true;
}
if (!sent) p->setZoomAndScroll(z, x, y);
} else printf("[jihad-bs] inject: bad cmd: %s", c.c_str());
} else if (c.compare(0, 7, "target ") != 0) printf("[jihad-bs] inject: bad cmd: %s", c.c_str());
}
}
}
+11
View File
@@ -14,8 +14,12 @@
#include <BrowserServerBase.h>
#include "../goanna/BrowserPageGoanna.h"
#include "../goanna/DownloadService.h" // jihad::DownloadSink
#include <atomic>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <thread>
#include <vector>
namespace jihad { class EngineHost; }
@@ -234,7 +238,14 @@ private:
std::vector<YapProxy*> mConnectOrder;
#ifndef JIHAD_NO_INJECT
std::string mInjectPath; // "" = channel disabled (default)
bool mInjectFocus = false; // "target focus": the focused card, not the newest
void (*mWakeHook)() = nullptr; // main()'s "leave the idle tick now"
// T-513 (review): the inject pan's and zoom's threads, stopped and joined at shutdown (the finished
// ones when the next starts)
struct InjectThread { std::thread thread; std::shared_ptr<std::atomic<bool>> done; };
std::vector<InjectThread> mInjectThreads;
void startInjectThread(std::function<void()> body);
std::atomic<bool> mInjectStop{false};
#endif
};
+166 -40
View File
@@ -819,7 +819,7 @@ bool BrowserPageGoanna::init(uint32_t width, uint32_t height,
// stale — the adapter re-announces once it has allocated the new one.
mKey3 = 0;
mBufSize = sharedBufferSize;
mActiveKey = mKey1;
mActiveKey = mKey1; mShownKey = 0;
mInFlight[0] = mInFlight[1] = mInFlight[2] = false; // fresh segments: nothing in flight (F-211)
// Attach-only: the adapter (isis IpcBuffer::create) makes each segment, IPC_RMID-marks
@@ -848,7 +848,9 @@ void BrowserPageGoanna::setWindowSize(uint32_t width, uint32_t height) {
xInvalidate();
mGpuZoom = 0.0; // T-502: the scroll port follows the card size; set again at the next zoom
mGpuAnchorValid = false;
mLayoutFollowed = false; // T-499: a new card size (rotation) starts from a layout viewport equal to it
// T-499: a new card size (rotation) starts from a layout viewport equal to it; the same size sent
// again (every NPAPI SetWindow) keeps the page's
if (!mPage || (int)width != mPage->Width() || (int)height != mPage->Height()) mLayoutFollowed = false;
// Same reasoning as the navigation reset: a resize replaces the whole frame, and both
// buffers still hold the old one.
mBufFull[0].valid = mBufFull[1].valid = mBufFull[2].valid = false;
@@ -980,7 +982,7 @@ void BrowserPageGoanna::thaw(int key1, int key2, int size) {
FrameLockGuard frameLock; // T-515
detachShm(); // the buffers may be new segments — drop stale cached attachments
mXConsumed[0] = mXConsumed[1] = mXConsumed[2] = 0;
mKey1 = key1; mKey2 = key2; mBufSize = size; mActiveKey = mKey1;
mKey1 = key1; mKey2 = key2; mBufSize = size; mActiveKey = mKey1; mShownKey = 0;
mKey3 = 0; // re-announced by the adapter after thaw; the old third segment may be gone
mInFlight[0] = mInFlight[1] = mInFlight[2] = false; // reattached (possibly new) segments: clear in flight (F-211)
// Possibly NEW segments — detachShm() above just dropped the old attachments — so nothing
@@ -1031,7 +1033,7 @@ void BrowserPageGoanna::returnBuffer(int sharedBufferKey) {
// from all five nav commands reproduces stock exactly.
void BrowserPageGoanna::resetScrollForNavigation() {
FrameLockGuard frameLock; // T-515: called from the pump too (a page's own navigation)
mLayoutFollowed = false; // T-499: the next page decides its own layout viewport
// (T-499: the next page's layout viewport is decided when its document is shown: emitGeometry)
// A new document invalidates both buffers wholesale. The damage-only path proves its
// premise from geometry (size, zoom, scroll), and a navigation can leave every one of
// those identical while replacing all the pixels — so geometry alone cannot catch it and
@@ -1206,8 +1208,9 @@ 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
xForget(mXJob2); xForget(mXJob); // T-513 (review): the express lane's frames too, or one recovering would write into a slot the software path paints
xInvalidate();
for (const GpuJob& j : mGpuJobs) mPage->ComposeForget(j.token);
for (const GpuJob& j : mGpuJobs) gpuForget(j);
mGpuJobs.clear();
for (int i = 0; i < 3; ++i) mGpuSlotValid[i] = false;
mGpuLatestSlot = -1;
@@ -2628,6 +2631,19 @@ bool BrowserPageGoanna::emitGeometry() {
// map wobbling by 0.5%). Fit-zoom exists to shrink WIDE desktop layouts; content that
// already fits the layout viewport should composite at exactly 1.0 (identity blit). The
// reported height is unchanged — only the width is floored at the window width.
// T-499: the layout viewport is a document's own. A new document (a navigation's, or the page's
// own) starts from the card's size: the old one's laid the next page out wide (2026-10-19: a
// width=device-width page loaded after a followed about:blank, at 980 px and a fit zoom of 0.787)
const uint64_t docKey = mPage->DocumentKey();
if (docKey != mLayoutDocKey) {
mLayoutDocKey = docKey; mLayoutFollowed = false;
if (mPage->LayoutWidth() != mPage->Width()) {
mPage->ResetLayoutSize();
cw = ch = 0;
got = mPage->GetContentSize(&cw, &ch);
mNeedsPaint = true; mDamageForceFull = true;
}
}
if (got && cw > 0 && cw < mPage->Width()) cw = mPage->Width();
// T-499: a page laid out wider than the card gets a layout viewport as wide as its content, ONCE
// per page and size (a reflow at the new width can change the content size; nothing follows that
@@ -2635,8 +2651,12 @@ bool BrowserPageGoanna::emitGeometry() {
// width is the card's business through initial-scale), only up to 1100 px (a desktop page's
// width; one overflowing element must not turn a phone layout into a desktop one), and only when
// the pref says so. Then innerWidth is the page's own width and what it parks just past that
// edge stays off screen while the card fit-zooms the whole width as before.
if (got && cw > mPage->Width() + 8 && cw <= 1100 && ch > 0 && ::gJihadViewportFollow && !mLayoutFollowed) {
// edge stays off screen while the card fit-zooms the whole width as before. Never about:blank (a
// new card's, there until the first page commits), and only once the document is parsed: until its
// <meta name=viewport> is seen the engine lays it out at 980 px as if it declared none (2026-10-19:
// a width=device-width page followed to 980 mid-parse and stayed at a fit zoom of 0.787).
if (got && cw > mPage->Width() + 8 && cw <= 1100 && ch > 0 && ::gJihadViewportFollow && !mLayoutFollowed &&
mPage->CurrentUri().compare(0, 11, "about:blank") != 0 && mPage->DocumentParsed()) {
double is = 1.0, mn = 1.0, mx = 1.0; int vw = 0, vh = 0; bool us = true;
bool declares = mPage->GetViewport(&is, &mn, &mx, &vw, &vh, &us);
mLayoutFollowed = true;
@@ -3412,6 +3432,16 @@ void BrowserPageGoanna::pump(int msBudget, bool drainOnly) {
// not returned the buffer, and frames actually painted.
void BrowserPageGoanna::maybePaint() {
if (!mGpuJobs.empty()) mGpuTrace[6]++;
if (jihad_offscreen_late_writes) {
// T-513 (review): a GPU read given up past its bound came back and wrote into a slot the
// software path may have painted (and the card may show): every slot painted whole again
const uint32_t lw = jihad_offscreen_late_writes();
if (lw != mLateWrites) {
mLateWrites = lw;
mBufFull[0].valid = mBufFull[1].valid = mBufFull[2].valid = false;
mDamageValid = false; mDamageForceFull = true; mNeedsPaint = true;
}
}
// T-515: an express frame at the held zoom is out (the main lane's own may be dropped for as long
// as the page's script runs): the displayport and the anchor follow the zoom now
if (mGpuZoomHoldMs && mXLastZoom > 0.0 && fabs(mXLastZoom - mGpuZoomHoldZ) < 1e-9)
@@ -3643,7 +3673,7 @@ void BrowserPageGoanna::gpuJobDrain() {
const int64_t t0 = jihadNowMs();
int r;
while ((r = mPage->ComposePoll(j.token, nullptr, nullptr)) <= -2 && jihadNowMs() - t0 < 3000) usleep(2000);
mPage->ComposeForget(j.token);
gpuForget(j);
}
}
mGpuJobs.clear();
@@ -3681,6 +3711,34 @@ static std::string jihadWhiteRuns(const unsigned char* px, int w, int h) {
return out;
}
// T-513: JIHAD_GPU_VERIFY's reference composites (gpuJobStart), every card's, under the frame lock.
// Each buffer is made once at the largest frame and never freed or resized: a given-up compose can
// still be read into its buffer late (the review's E5). One is taken again only once its reference was
// polled, or 5 s after it was given up (the engine's bound is 3 s); with none free a job gets no
// reference and the verify mode composes a late one as before.
namespace {
struct VerifyRef { std::vector<unsigned char> buf; bool busy = false; int64_t quarantineUntil = 0; };
VerifyRef sVerifyRef[6];
const size_t kVerifyRefBytes = (size_t)1024 * 1400 * 4; // the widest card by the frame's row cap
int verifyRefAcquire(size_t bytes) {
if (bytes > kVerifyRefBytes) return -1;
const int64_t now = jihadNowMs();
for (int i = 0; i < 6; ++i) {
VerifyRef& v = sVerifyRef[i];
if (v.busy || now < v.quarantineUntil) continue;
if (v.buf.empty()) v.buf.resize(kVerifyRefBytes);
v.busy = true;
return i;
}
return -1;
}
void verifyRefRelease(int i, bool givenUp) {
if (i < 0 || i >= 6) return;
sVerifyRef[i].busy = false;
if (givenUp) sVerifyRef[i].quarantineUntil = jihadNowMs() + 5000;
}
} // namespace
bool BrowserPageGoanna::gpuJobStart(int slot, int key, unsigned char* pixels, size_t bytes, int x, int y, double zoom, int w, int h) {
gpuSetView(zoom, mGpuViewRX, mGpuViewRY); // T-505: the view's row in this frame (the compositor takes it at the start)
long sLo = 0, sHi = h, s2Lo = 0, s2Hi = 0, shift = 0; // whole, unless the slot's frame is still good
@@ -3889,10 +3947,38 @@ bool BrowserPageGoanna::gpuJobStart(int slot, int key, unsigned char* pixels, si
j.win = win; // T-517
j.xFrames = mXFrames;
j.cmdDone = mXCmdDone;
// T-513 (R2's fast flick): JIHAD_GPU_VERIFY's reference, the whole frame composed right behind this
// job, on the same layer tree at the same view. Composed at the job's poll instead, it met a tree
// the pan had moved on from (the displayport's tiles above the view dropped) and called the right
// frames of a fast flick wrong. Its buffer is from a pool (sVerifyRef: never freed)
static const bool sVerify = getenv("JIHAD_GPU_VERIFY") != nullptr;
if (sVerify && (sHi - sLo < h || shift)) {
const size_t refBytes = (size_t)w * h * 4;
const int vb = verifyRefAcquire(refBytes);
if (vb >= 0) {
const uint32_t vt = mPage->ComposePost(sVerifyRef[vb].buf.data(), refBytes, h, 0, h, 0, 0, 0, nullptr, 0, false, nullptr, 0, -1, false, true);
if (vt) { j.vToken = vt; j.vBuf = vb; mXLastPostToken = vt; } // T-515: the newest composite is not a published frame
else verifyRefRelease(vb, false);
}
}
mGpuJobs.push_back(j);
return true;
}
// T-513: the buffer the card is told to show. The one it showed until now stays the adapter's until
// it has switched and returned it: its in-flight mark restarts here, or a mark older than the 2 s valve
// (a still page) would free it at once, while the adapter may still be blitting it (Codex 2026-10-24)
void BrowserPageGoanna::noteShown(int key) {
const int prev = slotForKey(mShownKey);
if (mShownKey && mShownKey != key && prev >= 0 && mInFlight[prev]) mPaintMs[prev] = jihadNowMs();
mShownKey = key;
}
void BrowserPageGoanna::gpuForget(const GpuJob& j) {
mPage->ComposeForget(j.token);
if (j.vToken) { mPage->ComposeForget(j.vToken); verifyRefRelease(j.vBuf, true); }
}
// The frame after the one in flight, into the slot after it, while the read thread has the
// first: the compositor composes one frame while the other is read back.
void BrowserPageGoanna::gpuJobStartNext(int x, int y, double zoom, int w, int h, size_t segSize, size_t hdr) {
@@ -3909,13 +3995,14 @@ void BrowserPageGoanna::gpuJobStartNext(int x, int y, double zoom, int w, int h,
// slot the adapter returns last is not waited for while another is free.
int key2 = keyAfter(mGpuJobs[0].key);
int slot2 = slotForKey(key2);
if (key2 == mShownKey) { key2 = keyAfter(key2); slot2 = slotForKey(key2); } // T-513 (review): never the shown buffer
if (slot2 >= 0 && mInFlight[slot2] && jihadNowMs() - mPaintMs[slot2] < 2000) {
const int key3 = keyAfter(key2), slot3 = slotForKey(key3);
if (slot3 >= 0 && key3 != mGpuJobs[0].key && !(mInFlight[slot3] && jihadNowMs() - mPaintMs[slot3] < 2000)) { key2 = key3; slot2 = slot3; }
}
int why = -1;
unsigned char* buf2 = nullptr;
if (slot2 < 0 || key2 == mGpuJobs[0].key) why = 0;
if (slot2 < 0 || key2 == mGpuJobs[0].key || key2 == mShownKey) why = 0;
else if (mInFlight[slot2] && jihadNowMs() - mPaintMs[slot2] < 2000) why = 1; // the adapter holds it
else if (!(buf2 = attachShm(key2))) why = 2;
else if (segSize < hdr + (size_t)w * h * 4) why = 3;
@@ -4086,6 +4173,10 @@ bool BrowserPageGoanna::xStart(int64_t now, double Z, const XWant& want, int64_t
for (int t = 0; t < 3 && slot < 0; ++t) {
if (!S.keys[t] || !S.base[t] || S.seg[t] < need) continue;
if (mInFlight[t] && now - mPaintMs[t] < 2000) continue;
// T-513 (review): never the buffer the card shows. The adapter keeps it until the next frame's
// message, and an aged in-flight mark (a page still for 2 s) or the adapter returning its own
// current buffer made it look free: a zoom's frame was composed into the buffer on screen
if (S.keys[t] == mShownKey) 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;
@@ -4103,7 +4194,7 @@ bool BrowserPageGoanna::xStart(int64_t now, double Z, const XWant& want, int64_t
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);
gpuForget(mGpuJobs[k]);
mGpuJobs.erase(mGpuJobs.begin() + k);
++superseded;
} else {
@@ -4297,7 +4388,7 @@ bool BrowserPageGoanna::xPoll(int64_t now) {
if ((sAvgN++ % 50) == 0)
JIHAD_GPU_LOG("[jihad-gpu] express frame times: on the slot's own frame %.1f ms, copied from the base %.1f ms\n", mXOwnMs, mXCopyMs);
}
mSink.msgPainted(mXJob.key);
mSink.msgPainted(mXJob.key); noteShown(mXJob.key);
mInFlight[s] = true; mPaintMs[s] = now;
jihadTimelinePublish("express", mXJob.t0, frameY, mXJob.zoom);
if (jihadTimelineOn()) {
@@ -4689,7 +4780,7 @@ void BrowserPageGoanna::paintToSharedBuffer() {
}
mLatestSlot = slot;
publishWindow(mActiveKey, 0); // T-517: the software path's frames start at the pixels' start
mSink.msgPainted(mActiveKey);
mSink.msgPainted(mActiveKey); noteShown(mActiveKey);
{
int ps = slotForKey(mActiveKey);
if (ps >= 0) { mInFlight[ps] = true; mPaintMs[ps] = jihadNowMs(); }
@@ -5124,6 +5215,16 @@ void BrowserPageGoanna::paintToSharedBuffer() {
}
} else {
const GpuJob oldest = mGpuJobs[0];
if (oldest.h != h || oldest.w != w) {
// T-513 (review): composed at another frame size (the overscan turned on at a document's
// first scroll while it was in flight): published at this tick's size its rows would not
// match the header. Given up; the next tick composes at this size
for (const GpuJob& j : mGpuJobs) gpuForget(j); // a second job builds on it
mGpuJobs.clear();
gpuSlotsLost();
mNeedsPaint = true;
return;
}
// T-517: the frame is in its window: every row this tick writes or reads is the window's
pixels += (size_t)oldest.win * (size_t)w * 4;
GoannaRenderPage::ComposeResult cr;
@@ -5173,6 +5274,17 @@ void BrowserPageGoanna::paintToSharedBuffer() {
return;
}
mGpuJobs.erase(mGpuJobs.begin());
// T-513: its verify reference's buffer goes back to the pool when this tick is done with it
// (given up, and kept aside a while, unless the verify below polled it complete)
struct VerifyRefForget {
GoannaRenderPage* p; uint32_t t; int b; bool taken;
~VerifyRefForget() {
if (!t) return;
if (taken) { verifyRefRelease(b, false); return; }
p->ComposeForget(t);
verifyRefRelease(b, true);
}
} vrf{mPage, oldest.vToken, oldest.vBuf, false};
nb = r;
// T-515: the express lane published after this job was posted: its frames are newer and its
// composite the newest; this one is dropped (its slot's record with it)
@@ -5268,35 +5380,48 @@ void BrowserPageGoanna::paintToSharedBuffer() {
// into scratch and compared row by row; the runs of rows that differ are logged, so a
// wrong shift, band or header shows as such
static const bool sVerify = getenv("JIHAD_GPU_VERIFY") != nullptr;
// (not a frame made at another zoom than the card's now: the verify composite uses the
// current view, so a frame from before a zoom change would compare as all wrong)
if (sVerify && (oldest.askHi - oldest.askLo < h || oldest.shift) && oldest.zoom == mZoom) {
// (a late reference, composed here, not of a frame made at another zoom than the card's
// now: it uses the current view, so a frame from before a zoom change would compare as all
// wrong; the reference composed behind the job has the job's own view)
if (sVerify && (oldest.askHi - oldest.askLo < h || oldest.shift) && (oldest.vToken || oldest.zoom == mZoom)) {
static std::vector<unsigned char> sScratch;
const size_t bytes = (size_t)w * h * 4;
if (sScratch.size() < bytes) sScratch.resize(bytes);
// the whole composite at the JOB's scroll (the pan has moved on since), then the
// card's current scroll again
const unsigned char* refPx = nullptr;
int r2 = -1;
const double vz = (mZoom >= 0.05 && mZoom <= 20.0) ? mZoom : 1.0;
const int viewRowNow = mGpuViewRow; // T-505: the job's own view row for its composite
mGpuViewRow = oldest.viewRow; gpuSetView(vz, mGpuViewRX, mGpuViewRY);
mPage->SetGpuScroll(oldest.x / vz, (oldest.y + oldest.viewRow) / vz, true);
const uint32_t vt = mPage->ComposeStart(sScratch.data(), bytes, 0, h, 0, 0, 0, nullptr, 0, false, true); // T-503: a verify composite
if (vt) mXLastPostToken = vt; // T-515: the newest composite is not a published frame
int r2 = -1;
if (vt) {
if (oldest.vToken) {
// T-513: the reference composed right behind the job, on its layer tree (gpuJobStart)
unsigned char* ref = sVerifyRef[oldest.vBuf].buf.data();
GoannaRenderPage::ComposeResult vr;
const int64_t t0 = jihadNowMs();
while ((r2 = mPage->ComposePoll(vt, sScratch.data(), &vr)) <= -2 && jihadNowMs() - t0 < 800) usleep(1000);
if (r2 <= -2) mPage->ComposeForget(vt);
while ((r2 = mPage->ComposePoll(oldest.vToken, ref, &vr)) <= -2 && jihadNowMs() - t0 < 800) usleep(1000);
if (r2 > -2) vrf.taken = true; // done: back to the pool once compared (vrf)
refPx = ref;
} else {
if (sScratch.size() < bytes) sScratch.resize(bytes);
refPx = sScratch.data();
// the whole composite at the JOB's scroll (the pan has moved on since), then the
// card's current scroll again
mGpuViewRow = oldest.viewRow; gpuSetView(vz, mGpuViewRX, mGpuViewRY);
mPage->SetGpuScroll(oldest.x / vz, (oldest.y + oldest.viewRow) / vz, true);
const uint32_t vt = mPage->ComposeStart(sScratch.data(), bytes, 0, h, 0, 0, 0, nullptr, 0, false, true); // T-503: a verify composite
if (vt) mXLastPostToken = vt; // T-515: the newest composite is not a published frame
if (vt) {
GoannaRenderPage::ComposeResult vr;
const int64_t t0 = jihadNowMs();
while ((r2 = mPage->ComposePoll(vt, sScratch.data(), &vr)) <= -2 && jihadNowMs() - t0 < 800) usleep(1000);
if (r2 <= -2) mPage->ComposeForget(vt);
}
mPage->SetGpuScroll(mAdapterScrollX / vz, mAdapterScrollY / vz, true);
mGpuViewRow = viewRowNow; gpuSetView(vz, mGpuViewRX, mGpuViewRY);
}
mPage->SetGpuScroll(mAdapterScrollX / vz, mAdapterScrollY / vz, true);
mGpuViewRow = viewRowNow; gpuSetView(vz, mGpuViewRX, mGpuViewRY);
if (r2 >= 0) {
std::string runs; int nRuns = 0, bad = 0;
for (int r = pageLo; r < pageHi; ) { // T-505: the page's rows (the rest is filled white)
if (memcmp(pixels + (size_t)r * w * 4, sScratch.data() + (size_t)r * w * 4, (size_t)w * 4) == 0) { ++r; continue; }
if (memcmp(pixels + (size_t)r * w * 4, refPx + (size_t)r * w * 4, (size_t)w * 4) == 0) { ++r; continue; }
int r0 = r;
while (r < pageHi && memcmp(pixels + (size_t)r * w * 4, sScratch.data() + (size_t)r * w * 4, (size_t)w * 4) != 0) ++r;
while (r < pageHi && memcmp(pixels + (size_t)r * w * 4, refPx + (size_t)r * w * 4, (size_t)w * 4) != 0) ++r;
bad += r - r0;
if (nRuns++ < 8) { char b[48]; snprintf(b, sizeof b, " %d..%d", r0, r); runs += b; }
}
@@ -5307,7 +5432,7 @@ void BrowserPageGoanna::paintToSharedBuffer() {
std::string wrongRuns; int nWrong = 0, wrongRows = 0;
auto rowWrong = [&](int r) {
const unsigned char* a = pixels + (size_t)r * w * 4;
const unsigned char* b = sScratch.data() + (size_t)r * w * 4;
const unsigned char* b = refPx + (size_t)r * w * 4;
int far = 0;
for (int x = 0; x < w && far <= 4; x++, a += 4, b += 4) {
if (abs(a[0] - b[0]) > 64 || abs(a[1] - b[1]) > 64 || abs(a[2] - b[2]) > 64) far++;
@@ -5327,7 +5452,7 @@ void BrowserPageGoanna::paintToSharedBuffer() {
if (wrongRows > 50 && !sDumpedWrong && getenv("JIHAD_GPU_VERIFY_DUMP_WRONG")) {
sDumpedWrong = true;
FILE* f1 = fopen("/tmp/vw-slot.raw", "wb"); if (f1) { fwrite(pixels, 1, bytes, f1); fclose(f1); }
FILE* f2 = fopen("/tmp/vw-whole.raw", "wb"); if (f2) { fwrite(sScratch.data(), 1, bytes, f2); fclose(f2); }
FILE* f2 = fopen("/tmp/vw-whole.raw", "wb"); if (f2) { fwrite(refPx, 1, bytes, f2); fclose(f2); }
JIHAD_GPU_LOG("[jihad-gpu] verify: the first wrong frame dumped (/tmp/vw-slot.raw, /tmp/vw-whole.raw)\n");
}
}
@@ -5344,7 +5469,7 @@ void BrowserPageGoanna::paintToSharedBuffer() {
const int rr = r + off;
if (rr < 0 || rr >= h) continue;
const unsigned char* a = pixels + (size_t)r * w * 4;
const unsigned char* b = sScratch.data() + (size_t)rr * w * 4;
const unsigned char* b = refPx + (size_t)rr * w * 4;
for (int x = 0; x < w; x++, a += 4, b += 4) sum += abs(a[0] - b[0]) + abs(a[1] - b[1]) + abs(a[2] - b[2]);
n += w * 3;
}
@@ -5368,7 +5493,7 @@ void BrowserPageGoanna::paintToSharedBuffer() {
if (structural > 0 && !sDumpedStruct && getenv("JIHAD_GPU_VERIFY_DUMP_WRONG")) {
sDumpedStruct = true;
FILE* f1 = fopen("/tmp/vs-slot.raw", "wb"); if (f1) { fwrite(pixels, 1, bytes, f1); fclose(f1); }
FILE* f2 = fopen("/tmp/vs-whole.raw", "wb"); if (f2) { fwrite(sScratch.data(), 1, bytes, f2); fclose(f2); }
FILE* f2 = fopen("/tmp/vs-whole.raw", "wb"); if (f2) { fwrite(refPx, 1, bytes, f2); fclose(f2); }
JIHAD_GPU_LOG("[jihad-gpu] verify: the first structurally wrong frame dumped (/tmp/vs-slot.raw, /tmp/vs-whole.raw)\n");
}
}
@@ -5381,8 +5506,9 @@ void BrowserPageGoanna::paintToSharedBuffer() {
// verify 2: the compositor against itself. The whole frame at (y - shift), shifted
// by the job's shift the way the slot was, against the whole frame at y: equal rows
// mean the content moves one frame row per card px, so a slot shifted right would
// match; rows that differ mean the compositor's scroll is not the card's.
if (r2 >= 0 && oldest.shift > 0 && oldest.shift < h) {
// match; rows that differ mean the compositor's scroll is not the card's. (T-513: with
// the late reference only: its own composite is late too, and the two compare alike)
if (r2 >= 0 && !oldest.vToken && oldest.shift > 0 && oldest.shift < h) {
static std::vector<unsigned char> sBefore;
if (sBefore.size() < bytes) sBefore.resize(bytes);
mGpuViewRow = oldest.viewRow; gpuSetView(vz, mGpuViewRX, mGpuViewRY);
@@ -5402,9 +5528,9 @@ void BrowserPageGoanna::paintToSharedBuffer() {
const size_t stride = (size_t)w * 4;
std::string runs; int nRuns = 0, bad = 0;
for (int r = 0; r + oldest.shift < h; ) {
if (memcmp(sBefore.data() + (size_t)(r + oldest.shift) * stride, sScratch.data() + (size_t)r * stride, stride) == 0) { ++r; continue; }
if (memcmp(sBefore.data() + (size_t)(r + oldest.shift) * stride, refPx + (size_t)r * stride, stride) == 0) { ++r; continue; }
int r0 = r;
while (r + oldest.shift < h && memcmp(sBefore.data() + (size_t)(r + oldest.shift) * stride, sScratch.data() + (size_t)r * stride, stride) != 0) ++r;
while (r + oldest.shift < h && memcmp(sBefore.data() + (size_t)(r + oldest.shift) * stride, refPx + (size_t)r * stride, stride) != 0) ++r;
bad += r - r0;
if (nRuns++ < 8) { char b[48]; snprintf(b, sizeof b, " %d..%d", r0, r); runs += b; }
}
@@ -5415,7 +5541,7 @@ void BrowserPageGoanna::paintToSharedBuffer() {
static long sPair = 0;
if (sDumpAt > 0 && ++sPair == sDumpAt) {
FILE* f1 = fopen("/tmp/v2-before.raw", "wb"); if (f1) { fwrite(sBefore.data(), 1, bytes, f1); fclose(f1); }
FILE* f2 = fopen("/tmp/v2-at.raw", "wb"); if (f2) { fwrite(sScratch.data(), 1, bytes, f2); fclose(f2); }
FILE* f2 = fopen("/tmp/v2-at.raw", "wb"); if (f2) { fwrite(refPx, 1, bytes, f2); fclose(f2); }
FILE* f3 = fopen("/tmp/v2-slot.raw", "wb"); if (f3) { fwrite(pixels, 1, bytes, f3); fclose(f3); }
JIHAD_GPU_LOG("[jihad-gpu] verify2 pair %ld dumped: whole at %d, whole at %d, the slot (shift %ld)\n", sPair, oldest.y - (int)oldest.shift, oldest.y, oldest.shift);
}
@@ -5744,7 +5870,7 @@ void BrowserPageGoanna::paintToSharedBuffer() {
// T-502 device rounds: the publish tick's time after the frame came in, around the message
const int64_t a = jihadNowMs();
publishWindow(mActiveKey, pubWin); // T-517: the frame's window (a GPU frame's; the software path's start at 0)
mSink.msgPainted(mActiveKey);
mSink.msgPainted(mActiveKey); noteShown(mActiveKey);
const int64_t b = jihadNowMs();
jihadTimelinePublish("main", gpuFinished ? paintT0 : 0, gpuFinished ? mGpuJobY : paintedTop, oi->contentZoom);
static int sN = 0;
+6
View File
@@ -351,6 +351,9 @@ private:
int64_t mLastWinResizeMs = 0;
int mLastWinResizeDh = 0; // new height - old height (>0 = grow, i.e. VKB down)
int mActiveKey; // which shm buffer we last painted into
int mShownKey = 0; // T-513: the buffer of the last msgPainted (the card shows it)
void noteShown(int key); // mShownKey moves on; the previous one's in-flight mark restarts
uint32_t mLateWrites = 0; // T-513: jihad_offscreen_late_writes() as last seen
// Cache of attached shared buffers (up to the 2 alternating segments). shmat/shmdt of the ~12MB
// segment on EVERY paint cost ~400ms/keystroke on the device; attach once and reuse instead.
unsigned char* mShmBuf[3];
@@ -421,7 +424,9 @@ private:
uint32_t xFrames = 0; // T-515: the express lane's frames when it was posted (newer ones since: dropped)
uint32_t cmdDone = 0; // T-515: the card's commands the main loop had handled when it was posted
int win = 0; // T-517: the frame's window in its slot (rows into the pixels)
uint32_t vToken = 0; int vBuf = -1; // T-513: JIHAD_GPU_VERIFY's reference, composed right behind the job
};
void gpuForget(const GpuJob& j); // the job and its verify reference given up
// T-505: the view's row in the GPU frame and the residual offset last asked of the compositor
int mGpuViewRow = 0;
int mGpuJobViewRow = 0; // T-507: the published GPU frame's view row (thumbnails)
@@ -658,6 +663,7 @@ private:
bool mPopupWasOpen = false; // a XUL popup was open at the last pump (repaint on change)
bool mViewportSent = false;
bool mLayoutFollowed = false; // T-499: this page already got its layout viewport
uint64_t mLayoutDocKey = 0; // T-499: the document that is about (DocumentKey)
double mVpIs = 0, mVpMn = 0, mVpMx = 0; int mVpW = 0, mVpH = 0; bool mVpUs = true;
int mLastScrollX, mLastScrollY; // last emitted scroll offset
double mZoom; // current full-page zoom (for input coord mapping)
+1
View File
@@ -75,6 +75,7 @@ extern "C" {
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_late_writes() __attribute__((weak)); // T-513: given-up reads that wrote into a slot
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,
+60 -7
View File
@@ -1095,15 +1095,65 @@ bool GoannaRenderPage::SetLayoutSize(int width, int height) {
return true;
}
// T-499: the engine's window back to the card's size, as Resize() leaves it (a new document decides
// its own layout viewport)
void GoannaRenderPage::ResetLayoutSize() {
if (!mChrome || !mLayoutW) return;
mLayoutW = mLayoutH = 0;
if (mOffscreen) jihad_offscreen_resize(mWidget, mWidth, mHeight);
nsCOMPtr<nsIBaseWindow> bw = do_QueryInterface(mChrome->mBrowser);
if (bw) bw->SetPositionAndSize(0, 0, mWidth, mHeight, nsIBaseWindow::eRepaint);
nsCOMPtr<nsIDocShell> ds = GetDocShell(mChrome->mBrowser);
if (ds) {
nsCOMPtr<nsIPresShell> ps = ds->GetPresShell();
if (ps) engine_calls::ResizeReflow(ps.get(), mWidth, mHeight);
}
JIHAD_GTK_PUMP();
fprintf(stderr, "[jihad-bs] layout viewport back to the card %dx%d (a new document)\n", mWidth, mHeight);
}
static nsCOMPtr<nsIDOMDocument> ShownDocument(PageChrome* chrome) {
nsCOMPtr<nsIDOMDocument> doc;
if (!chrome) return doc;
nsCOMPtr<nsIDocShell> ds = GetDocShell(chrome->mBrowser);
if (!ds) return doc;
nsCOMPtr<nsIContentViewer> cv; ds->GetContentViewer(getter_AddRefs(cv));
if (cv) cv->GetDOMDocument(getter_AddRefs(doc));
return doc;
}
// The current inner window's id: one per document, never reused (a document's address can be, once
// it is freed: Codex 2026-10-24)
uint64_t GoannaRenderPage::DocumentKey() const {
if (!mChrome) return 0;
nsCOMPtr<nsIDOMWindowUtils> u = GetWindowUtils(mChrome->mBrowser);
uint64_t id = 0;
if (!u || NS_FAILED(u->GetCurrentInnerWindowID(&id))) return 0;
return id;
}
// T-499: the shown document is parsed (readyState past "loading"): its <meta name=viewport>, if it
// has one, has been seen. Before that the engine lays a document out at its 980-px desktop width,
// as if it declared none.
bool GoannaRenderPage::DocumentParsed() const {
nsCOMPtr<nsIDOMDocument> doc = ShownDocument(mChrome);
nsAutoString rs;
return doc && NS_SUCCEEDED(doc->GetReadyState(rs)) && !rs.IsEmpty() && !rs.EqualsLiteral("loading");
}
bool GoannaRenderPage::Resize(int width, int height) {
if (!mChrome) return false;
if ((mOffscreen && !mWidget) || (!mOffscreen && !mWindow)) return false;
if (width <= 0 || height <= 0 || width > 8192 || height > 8192) return false;
// T-499: a new card size starts from a layout viewport equal to it; the card's own size sent again
// keeps the page's (the adapter sends the size on every NPAPI SetWindow, the same size included,
// and each put the engine back at the card's width: DuckDuckGo read innerWidth 768 again)
if (width != mWidth || height != mHeight) mLayoutW = mLayoutH = 0;
mWidth = width; mHeight = height;
mLayoutW = mLayoutH = 0; // T-499: a new card size starts from a layout viewport equal to it
mChrome->mW = width; mChrome->mH = height; // keep GetDimensions() consistent
const int ew = mLayoutW ? mLayoutW : width, eh = mLayoutW ? mLayoutH : height; // the engine's window
if (mOffscreen) {
jihad_offscreen_resize(mWidget, width, height);
jihad_offscreen_resize(mWidget, ew, eh);
} else {
#ifndef JIHAD_OFFSCREEN_ONLY
gtk_widget_set_size_request(mWindow, width, height);
@@ -1113,7 +1163,7 @@ bool GoannaRenderPage::Resize(int width, int height) {
// Resize the embedded surface; eRepaint invalidates so the page reflows to the
// new viewport (100vw/100vh content follows the new size).
nsCOMPtr<nsIBaseWindow> bw = do_QueryInterface(mChrome->mBrowser);
if (bw) bw->SetPositionAndSize(0, 0, width, height, nsIBaseWindow::eRepaint);
if (bw) bw->SetPositionAndSize(0, 0, ew, eh, nsIBaseWindow::eRepaint);
// Force the document to actually REFLOW at the new size. In this headless embedding
// the widget/baseWindow resize does not propagate to a presShell resize-reflow (there
// is no nsView listener wired up), so after a rotate the page stayed laid out at the
@@ -1129,7 +1179,7 @@ bool GoannaRenderPage::Resize(int width, int height) {
// devicePixelRatio is pinned to 1 (layout.css.devPixelsPerPx), so 1 device px ==
// 1 CSS px == 60 app units. (nsPresContext.h can't be included here -- it drags in
// internal string headers unusable from the frozen-API daemon.)
engine_calls::ResizeReflow(ps.get(), width, height);
engine_calls::ResizeReflow(ps.get(), ew, eh);
}
}
JIHAD_GTK_PUMP();
@@ -4749,13 +4799,14 @@ bool GoannaRenderPage::PaintLayers() {
uint32_t GoannaRenderPage::ComposePost(unsigned char* dst, size_t dstBytes, int frameH, long rowLo, long rowHi, long row2Lo,
long row2Hi, long shift, const unsigned char* src, long srcY, bool engineShift,
const double* aView, long colX0, long colX1, bool windowMoved) {
const double* aView, long colX0, long colX1, bool windowMoved, bool verify) {
if (!ExpressReady() || !mWidget || !dst) return 0;
const int w = mWidth;
if (w <= 0 || frameH <= 0 || dstBytes < (size_t)w * frameH * 4) return 0;
if (colX1 < 0) { colX0 = 0; colX1 = w; } // whole rows
return jihad_offscreen_compose_post(mWidget, dst, w * 4, w, frameH, (int)rowLo, (int)rowHi, (int)row2Lo, (int)row2Hi,
(int)shift, src, (int)srcY, (engineShift ? 1 : 0) | 4 | (windowMoved ? 8 : 0), aView ? 1 : 0,
(int)shift, src, (int)srcY,
(engineShift ? 1 : 0) | (verify ? 2 : 0) | 4 | (windowMoved ? 8 : 0), aView ? 1 : 0,
aView ? aView[0] : 1.0, aView ? aView[1] : 0.0, aView ? aView[2] : 0.0,
aView ? aView[3] : 0.0, aView ? aView[4] : 0.0, aView ? 1 : 0, (int)colX0, (int)colX1);
}
@@ -4820,7 +4871,9 @@ int GoannaRenderPage::ComposePoll(uint32_t token, unsigned char* dst, ComposeRes
}
void GoannaRenderPage::ComposeForget(uint32_t token) {
if (mWidget && ComposeAsync() && token) jihad_offscreen_compose_forget(mWidget, token);
// T-513 (review): not gated on the GPU path being on: a job forgotten after DisableGpu must still
// reach the engine, which then writes nothing into its slot
if (mWidget && token && jihad_offscreen_compose_forget) jihad_offscreen_compose_forget(mWidget, token);
}
long GoannaRenderPage::ReadPixels(unsigned char* dst, size_t dstBytes) {
+7 -2
View File
@@ -170,9 +170,14 @@ public:
void SetActive(bool active);
// T-499: the engine's layout viewport set WIDER than the card (an offscreen window as wide as the
// page's overflow), so innerWidth and position:fixed follow the page's own width while the card
// fit-zooms the whole of it; Width()/Height() stay the card's. Resize() puts it back.
// fit-zooms the whole of it; Width()/Height() stay the card's. Resize() to a new card size and
// ResetLayoutSize() put it back. DocumentKey: the shown document's identity (0 without one), so a
// new document starts from the card's size.
bool SetLayoutSize(int width, int height);
void ResetLayoutSize();
int LayoutWidth() const { return mLayoutW ? mLayoutW : mWidth; }
uint64_t DocumentKey() const;
bool DocumentParsed() const;
// Scroll the content to an absolute CSS-pixel offset (YAP: setScrollPosition).
void ScrollTo(int x, int y);
@@ -388,7 +393,7 @@ public:
bool PaintLayers();
uint32_t ComposePost(unsigned char* dst, size_t dstBytes, int frameH, long rowLo, long rowHi, long row2Lo, long row2Hi,
long shift, const unsigned char* src, long srcY, bool engineShift, const double* aView,
long colX0 = 0, long colX1 = -1, bool windowMoved = false);
long colX0 = 0, long colX1 = -1, bool windowMoved = false, bool verify = false);
bool ExpressReady() const;
int ComposePeek(uint32_t token); // 1 done, 2 composed (being read), 0 pending, -1 unknown
// askLo/askHi: the band that job was started with (what was read is it joined with the change)
+7
View File
@@ -99,6 +99,13 @@
app.$.browser.reloadClick();
log(name, "reload " + String(app.$.browser.url).slice(0, 80));
wait = 4000;
} else if (st === "A") { // bring the app's other card to the front (two cards driven in
// turn: this card's own timers stop once it is behind, so A is its last useful step)
var ws = enyo.windows.getWindows(), other = null;
for (var wn in ws) { if (ws[wn] && ws[wn] !== window) { other = ws[wn]; } }
if (other) { enyo.windows.activateWindow(other); }
log(name, other ? "activated the other card" : "no other card to activate");
wait = 3000;
} else if (st.charAt(0) === "O") { // Oup | Oleft | Oright | Odown | Ofree: hold the card in that
// orientation (enyo.setAllowedOrientation: the system rotates the window), then report the size
var o = st.slice(1);
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<!-- Jihad Browser test page (cavekit-modern-web.md R6, T-499): a page laid out 1000 CSS px wide with
no meta viewport, as DuckDuckGo's results page is. With the layout viewport following the page's
overflow (jihad.viewport.followOverflow), innerWidth reads 1000 and the panel parked at
innerWidth + 4 (as DuckDuckGo parks its slide-out menu) stays off the card; with the layout
viewport at the card's 768 the panel shows at the right edge of the fit-zoomed page. The title
carries what the page measures, every second (the daemon logs title changes).
Serve it over http from the host PC (python3 -m http.server 8077 in render/goanna/test/).
Copyright 2026 NotAlexNoyle. Apache-2.0. -->
<html><head><meta charset="utf-8"><title>viewport-wide</title>
<style>
body { margin: 0; font: 18px sans-serif; }
#wide { width: 1000px; height: 1400px; background: linear-gradient(90deg, #cde 0, #cde 768px, #edc 768px, #edc 1000px); }
#parked { position: fixed; top: 40px; left: calc(100vw + 4px); width: 200px; height: 300px; background: #c00; color: #fff; }
</style></head>
<body><div id="wide"><p id="m">measuring</p></div><div id="parked">parked past innerWidth: must not show</div>
<script>
function measure() {
var p = document.getElementById("parked").getBoundingClientRect();
var t = "iw=" + innerWidth + " cw=" + document.documentElement.clientWidth + " sw=" + screen.width +
" parked.x=" + Math.round(p.left);
document.title = t;
document.getElementById("m").textContent = t;
}
measure();
setInterval(measure, 1000);
</script></body></html>
+1 -1
+10
View File
@@ -198,6 +198,16 @@ the daemon (`render/goanna/JihadGpuLog.h`); failures and fallbacks print anyway.
close prints one line with its express-lane frame count (`page closed: <n> frames by the express lane`),
which `default-check.sh` counts.
T-513 (R2's fast flick and two cards): `JIHAD_GPU_VERIFY=1` composes its reference right behind each partial
job, on the same layer tree, and compares at the job's publish (a reference composed later met a tree a fast
flick had moved on from and called right frames wrong). `two-cards.sh <url2> [pan]`, after
`gpu-spike-start.sh <url1> "JIHAD_INJECT=1 JIHAD_GPU_VERIFY=1"`: a fast flick on card 1, the app relaunched
on url2 (a second card whose Enyo test hook brings card 1 back after 28 s with its `A` step: push the hook
first, `card-selftest.sh enyo push`), a flick on card 2, then on card 1 again through the inject channel's
`target focus` (commands to the card that last said it has the focus; `target last` goes back to the newest
connection). The spike start sets `jihad.viewport.followOverflow` false; `PREF=jihad.viewport.followOverflow=true`
runs with the shipped rule.
Older device-round switches still in the code (T-502), for completeness: `JIHAD_GPU_DUMP2=<path>` (the 5th
frame's buffer as the adapter reads it), `JIHAD_GPU_DUMP_SEQ=<path>` (the first 12 content frames, every
+18 -15
View File
@@ -4,27 +4,30 @@ echo "=== LOG END"
# T-502 spike: kill the hand-started Enyo daemon and put the upstart job back.
APP=/media/cryptofs/apps/usr/palm/applications/net.riverstonerelay.jihad-browser
HL=$APP/deviceroot/hl
for p in /proc/[0-9]*; do
c=$(tr '\0' ' ' < $p/cmdline 2>/dev/null)
case "$c" in *jihad-browserserver*"$HL"*) kill ${p#/proc/} 2>/dev/null; echo "killed ${p#/proc/}";; esac
done
sleep 2
PREFS=$APP/profile/prefs.js
gone() { for p in /proc/[0-9]*; do case "$(tr '\0' ' ' 2>/dev/null < $p/cmdline)" in *jihad-browserserver*"$HL"*) return 1;; esac; done; return 0; }
killall_hl() { for p in /proc/[0-9]*; do case "$(tr '\0' ' ' 2>/dev/null < $p/cmdline)" in *jihad-browserserver*"$HL"*) kill $1 ${p#/proc/} 2>/dev/null; echo "killed ${p#/proc/} $1";; esac; done; }
killall_hl
# The daemon saves its prefs on the way out: the profile is touched only once it is gone, or the
# spike's followOverflow=false came back and stayed in the shipped profile (found 2026-10-19: T-499
# was off on the Enyo card for hours after a spike). SIGTERM first (a daemon in the GPU driver), then
# SIGKILL; still there, the profile is left alone and the upstart job is not started over it.
i=0; while [ $i -lt 30 ] && ! gone; do sleep 1; i=$((i+1)); done
if ! gone; then echo "the hand-started daemon outlived SIGTERM by 30 s: SIGKILL"; killall_hl -9; i=0; while [ $i -lt 5 ] && ! gone; do sleep 1; i=$((i+1)); done; fi
if ! gone; then echo "ERROR: the hand-started daemon is still running; the profile and the upstart job are left as they are"; exit 1; fi
# a daemon killed by hand leaves the clock floor at the top: put it back
for c in 0 1; do d=/sys/devices/system/cpu/cpu$c/cpufreq; [ -d $d ] && cat $d/cpuinfo_min_freq > $d/scaling_min_freq 2>/dev/null; done
sed -i '/jihad.viewport.followOverflow/d' $APP/profile/prefs.js 2>/dev/null
stop jihad >/dev/null 2>&1; sleep 1; start jihad >/dev/null 2>&1; sleep 4
status jihad
n=0; for p in /proc/[0-9]*; do c=$(tr '\0' ' ' < $p/cmdline 2>/dev/null); case "$c" in *jihad-browserserver*"$HL"*) n=$((n+1));; esac; done
echo "enyo daemons running: $n"
# the prefs a run added (PREF= words) go again, now that the daemon is stopped
PREFS=/media/cryptofs/apps/usr/palm/applications/net.riverstonerelay.jihad-browser/profile/prefs.js
# the run's own settings go before the upstart daemon starts (one that starts on them saves them back)
sed -i '/jihad.viewport.followOverflow/d' "$PREFS" 2>/dev/null
# the prefs a run added (PREF= words)
if [ -s /tmp/spike-prefs.txt ]; then
while read n; do [ -n "$n" ] && sed -i "/\"$n\"/d" "$PREFS" && echo "pref stripped: $n"; done < /tmp/spike-prefs.txt
: > /tmp/spike-prefs.txt
fi
# the default prefs a run changed (GPREF= words): the bundle's goanna.js back
HL=/media/cryptofs/apps/usr/palm/applications/net.riverstonerelay.jihad-browser/deviceroot/hl
if [ -f "$HL/goanna.js.spike-orig" ]; then mv "$HL/goanna.js.spike-orig" "$HL/goanna.js" && echo "goanna.js restored"; fi
: > /tmp/spike-gprefs.txt
stop jihad >/dev/null 2>&1; sleep 1; start jihad >/dev/null 2>&1; sleep 4
status jihad
n=0; for p in /proc/[0-9]*; do c=$(tr '\0' ' ' 2>/dev/null < $p/cmdline); case "$c" in *jihad-browserserver*"$HL"*) n=$((n+1));; esac; done
echo "enyo daemons running: $n"
+13 -3
View File
@@ -23,10 +23,20 @@ done
sleep 2
stop jihad >/dev/null 2>&1; sleep 2
for p in /proc/[0-9]*; do
c=$(tr '\0' ' ' < $p/cmdline 2>/dev/null)
c=$(tr '\0' ' ' 2>/dev/null < $p/cmdline)
case "$c" in *jihad-browserserver*"$HL"*) kill ${p#/proc/} 2>/dev/null;; esac
done
sleep 1
# The pref edits below wait for every Enyo daemon to be gone: one still exiting saves its prefs over
# them (2026-10-19: the spike's followOverflow=false was lost that way, T-499 on in a spike). SIGTERM
# had 30 s, then SIGKILL; one still there and the run does not start.
gone() { for p in /proc/[0-9]*; do case "$(tr '\0' ' ' 2>/dev/null < $p/cmdline)" in *jihad-browserserver*"$HL"*) return 1;; esac; done; return 0; }
i=0; while [ $i -lt 30 ] && ! gone; do sleep 1; i=$((i+1)); done
if ! gone; then
echo "an Enyo daemon outlived SIGTERM by 30 s: SIGKILL"
for p in /proc/[0-9]*; do case "$(tr '\0' ' ' 2>/dev/null < $p/cmdline)" in *jihad-browserserver*"$HL"*) kill -9 ${p#/proc/} 2>/dev/null;; esac; done
i=0; while [ $i -lt 5 ] && ! gone; do sleep 1; i=$((i+1)); done
fi
if ! gone; then echo "FATAL: an Enyo daemon is still running; the spike does not start"; exit 1; fi
: > "$LOG"
rm -f /tmp/frame.raw /tmp/frames.raw
# T-499 off for the spike: the widget stays at the card's size (no 980x1202 layout viewport, no fit zoom)
@@ -75,7 +85,7 @@ echo "--- pipeline"
grep -a "paint pipeline\|gap hist\|tick period\|seq frame" "$LOG" | tail -n 24
echo "--- daemon alive?"
for p in /proc/[0-9]*; do
c=$(tr '\0' ' ' < $p/cmdline 2>/dev/null)
c=$(tr '\0' ' ' 2>/dev/null < $p/cmdline)
case "$c" in *jihad-browserserver*"$HL"*) echo "pid ${p#/proc/} rss $(grep VmRSS $p/status)";; esac
done
echo "--- log tail"
+2 -2
View File
@@ -14,12 +14,12 @@ cat > $S/pan-inject.sh <<'EOS'
STATE=/var/palm/jihad/enyo
rm -f /tmp/frame.raw-*
sleep PANDELAY # PAN_DELAY seconds for a page that is still loading
for p in /proc/[0-9]*; do c=$(tr '\0' ' ' < $p/cmdline 2>/dev/null); case "$c" in *jihad-browserserver*) echo "daemon env: $(tr '\0' '\n' < $p/environ 2>/dev/null | grep 'DUMP\|INJECT' | tr '\n' ' ')";; esac; done
for p in /proc/[0-9]*; do c=$(tr '\0' ' ' 2>/dev/null < $p/cmdline); case "$c" in *jihad-browserserver*) echo "daemon env: $(tr '\0' '\n' < $p/environ 2>/dev/null | grep 'DUMP\|INJECT' | tr '\n' ' ')";; esac; done
: > $STATE/daemon-gpu.pan-mark
[ "PANCMD" != "none" ] && { umask 077; echo "PANCMD" > $STATE/inject.cmd; } # the daemon consumes the file at once
if [ "PANTOP" = 1 ]; then
sleep 1; top -b -n 2 -d 4 > /tmp/top-pan.txt 2>&1 &
for p in /proc/[0-9]*; do c=$(tr '\0' ' ' < $p/cmdline 2>/dev/null); case "$c" in *jihad-browserserver*) DP=${p#/proc/};; esac; done
for p in /proc/[0-9]*; do c=$(tr '\0' ' ' 2>/dev/null < $p/cmdline); case "$c" in *jihad-browserserver*) DP=${p#/proc/};; esac; done
for t in /proc/$DP/task/*; do echo "$(cat $t/comm) $(sed 's/.*) //' $t/stat | cut -d' ' -f12,13)"; done > /tmp/ts0.txt
( sleep 4; for t in /proc/$DP/task/*; do echo "$(cat $t/comm) $(sed 's/.*) //' $t/stat | cut -d' ' -f12,13)"; done > /tmp/ts1.txt ) &
fi
+30
View File
@@ -0,0 +1,30 @@
# T-513 (R2's fast flick and two cards in turn), run after gpu-spike-start.sh opened card 1 with
# JIHAD_INJECT=1 JIHAD_GPU_VERIFY=1: a fast flick on card 1, then the app relaunched on <url2> (a
# second card, whose test hook brings card 1 back after its wait), a fast flick on card 2, then one on
# card 1 again through "target focus". Every verify line is printed under the pan it follows.
# dev-sh.sh -t 200 tools/device/t502/two-cards.sh <url2> ["pan 0 8000 800"]
# Needs the Enyo test hook with its A step (build/webos-oe/card-selftest.sh enyo push).
URL2="$1"; shift
PAN="${*:-pan 0 8000 800}"
ID=net.riverstonerelay.jihad-browser
STATE=/var/palm/jihad/enyo; L=$STATE/daemon-gpu.log
inject() { umask 077; printf '%s\n' "$@" > $STATE/inject.cmd; }
section() {
echo "=== $1"
tail -n +$(($2 + 1)) $L | grep -a 'inject\|verify\|focus:\|client connected\|FATAL\|SIGSEGV' | cut -c1-230 | head -n 40
}
m0=$(wc -l < $L)
inject "$PAN"; sleep 8
section "card 1, a fast flick ($PAN)" $m0
luna-send -n 1 palm://com.palm.applicationManager/launch '{"id":"'$ID'","params":{"target":"'"$URL2"'","jihadTest":"page:w28;A"}}' >/dev/null 2>&1
sleep 20
m1=$(wc -l < $L)
inject "target last" "$PAN"; sleep 8
section "card 2 ($URL2), a fast flick" $m1
sleep 20 # card 2's hook brings card 1 to the front
m2=$(wc -l < $L)
inject "target focus" "$PAN"; sleep 8
section "card 1 again (target focus), a fast flick" $m2
echo "=== totals"
tail -n +$((m0 + 1)) $L | grep -a 'verify slot' | awk '{for (i=1;i<=NF;i++) if ($i=="rows" && $(i+1)=="wrong:") w+=$(i-1); else if ($i=="rows" && $(i+1)=="structurally") s+=$(i-1); n++} END {printf "%d verify lines, %d rows wrong, %d rows structurally wrong\n", n, w, s}'
( cat /var/log/messages.0 /var/log/messages 2>/dev/null ) | grep 'JIHAD-TEST\] page:w28;A' | sed 's/^[0-9-]*T\([0-9:]*\)[^ ]* .*\(JIHAD-[A-Z]*\]\)/\1 \2/; s/, file:.*//' | tail -4