diff --git a/build/desktop/patches/0064-widget-a-GPU-path-layer-paint-with-nothing-pending-i.patch b/build/desktop/patches/0064-widget-a-GPU-path-layer-paint-with-nothing-pending-i.patch new file mode 100644 index 0000000..3ef0d38 --- /dev/null +++ b/build/desktop/patches/0064-widget-a-GPU-path-layer-paint-with-nothing-pending-i.patch @@ -0,0 +1,88 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NotAlexNoyle +Date: Mon, 26 Oct 2026 00:01:48 -0400 +Subject: [PATCH] widget: a GPU-path layer paint with nothing pending is + skipped; a moved displayport base schedules a paint + +T-527. JihadPaintLayers returns at once after the first paint when the widget has no invalid region, +no paint task pending and the refresh driver owes no view-manager flush: the layer tree is current +and a paint would only build the display list again (JIHAD_GPU_PAINT_ALWAYS=1 paints every time). +jihad_layout_set_displayport2 schedules a paint of the root scroll frame when the base moved +(SetDisplayPortMargins compares displayports computed with the new base on both sides). + +Co-Authored-By: Claude Opus 5.5 +--- + layout/base/nsLayoutUtils.cpp | 7 +++++++ + widget/PuppetWidget.cpp | 20 ++++++++++++++++++++ + widget/PuppetWidget.h | 1 + + 3 files changed, 28 insertions(+) + +diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp +index 036734c9eb..026f65cd6a 100644 +--- a/layout/base/nsLayoutUtils.cpp ++++ b/layout/base/nsLayoutUtils.cpp +@@ -9484,7 +9484,14 @@ jihad_layout_set_displayport2(nsIDocShell* aDocShell, float aVisW, float aVisH, + const float baseH = std::max(aVisH, aShiftY > 0.0f ? 0.0f : portH); + const nsRect base(nsPresContext::CSSPixelsToAppUnits(aShiftX * res), nsPresContext::CSSPixelsToAppUnits(aShiftY * res), + nsPresContext::CSSPixelsToAppUnits(baseW * res), nsPresContext::CSSPixelsToAppUnits(baseH * res)); ++ // T-527: a new base is a new displayport even when the margins are the same (SetDisplayPortMargins ++ // compares the displayport computed with the new base on both sides and schedules no paint) ++ const nsRect* oldBase = static_cast(content->GetProperty(nsGkAtoms::DisplayPortBase)); ++ const bool baseMoved = !oldBase || !oldBase->IsEqualEdges(base); + nsLayoutUtils::SetDisplayPortBase(content, base); ++ if (baseMoved) { ++ rsf->SchedulePaint(); ++ } + const ScreenMargin margins(aTop * res, aSide * res, aBottom * res, aSide * res); + static int sLogged = 0; + if (sLogged++ < 12) { +diff --git a/widget/PuppetWidget.cpp b/widget/PuppetWidget.cpp +index b056433871..1e32b2e6eb 100644 +--- a/widget/PuppetWidget.cpp ++++ b/widget/PuppetWidget.cpp +@@ -50,6 +50,7 @@ namespace mozilla { namespace layers { extern std::atomic gJihadLateWr + // jihad_offscreen_composite_popups at the bottom of this file): + #include "nsXULPopupManager.h" + #include "nsLayoutUtils.h" ++#include "nsRefreshDriver.h" // T-527: a paint already scheduled (JihadPaintLayers) + #include "nsRenderingContext.h" + #include "nsMenuPopupFrame.h" + #include "nsMenuFrame.h" +@@ -967,6 +968,25 @@ PuppetWidget::JihadPaintLayers(nsIDocShell* aDocShell) + if (!mozilla::layers::CompositorThreadHolder::Loop()) { + return false; + } ++ // T-527: nothing to paint — no invalidation waiting (the widget's region, its paint task), no paint ++ // the refresh driver has scheduled, the layers painted before: the layer tree is current, and a paint ++ // would only build the whole display list again (desktop Wikipedia: 16-90 ms of the main thread; the ++ // daemon asks for a layer paint every 40 ms during a pan and right after the refresh driver's own ++ // paint of a zoom). JIHAD_GPU_PAINT_ALWAYS=1 paints every time, as before. ++ { ++ static const bool sAlways = getenv("JIHAD_GPU_PAINT_ALWAYS") != nullptr; ++ nsPresContext* pc = presShell->GetPresContext(); ++ nsRefreshDriver* rd = pc ? pc->RefreshDriver() : nullptr; ++ if (!sAlways && mJihadLayersPainted && mDirtyRegion.IsEmpty() && !mPaintTask.IsPending() && rd && ++ !rd->ViewManagerFlushIsPending()) { ++ static uint32_t sSkipped = 0; ++ if ((++sSkipped % 100) == 1) { ++ JIHAD_GPU_LOG("[jihad-gpu] layer paint skipped: nothing pending (%u so far)\n", sSkipped); ++ } ++ return true; ++ } ++ } ++ mJihadLayersPainted = true; + mozilla::TimeStamp t0 = mozilla::TimeStamp::Now(); + nsRegion dirty(root->GetDimensions()); + presShell->Paint(root, dirty, nsIPresShell::PAINT_LAYERS); +diff --git a/widget/PuppetWidget.h b/widget/PuppetWidget.h +index 7b570ef46d..755f67de5a 100644 +--- a/widget/PuppetWidget.h ++++ b/widget/PuppetWidget.h +@@ -459,6 +459,7 @@ private: + RefPtr mChild; + LayoutDeviceIntRegion mDirtyRegion; + bool mJihadDirty = false; // sticky Invalidate() flag, drained by JihadTakeDirty() ++ bool mJihadLayersPainted = false; // T-527: JihadPaintLayers has painted this widget's layers once + bool mJihadCompositorTried = false; // T-502: CreateCompositor attempted once per widget + bool mJihadSharedLayerManager = false; // T-502: mLayerManager is the parent's (a child widget) + float mJihadScale = 1.0f; // T-502: the card's zoom, applied by the compositor diff --git a/build/desktop/patches/0065-gpu-compositor-a-band-surface-that-cannot-be-made-at.patch b/build/desktop/patches/0065-gpu-compositor-a-band-surface-that-cannot-be-made-at.patch new file mode 100644 index 0000000..7c34a70 --- /dev/null +++ b/build/desktop/patches/0065-gpu-compositor-a-band-surface-that-cannot-be-made-at.patch @@ -0,0 +1,59 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NotAlexNoyle +Date: Mon, 26 Oct 2026 00:01:48 -0400 +Subject: [PATCH] gpu-compositor: a band surface that cannot be made at a width + is not tried again every frame + +T-527 (review). A pbuffer or render target that failed at a width was re-created, and a line logged, +on every band frame (~25 a second into the device's small /var). The width it failed at is kept per +surface and the frame's own surface does the pass. + +Co-Authored-By: Claude Opus 5.5 +--- + gfx/layers/opengl/CompositorOGL.cpp | 10 +++++++++- + gfx/layers/opengl/CompositorOGL.h | 1 + + 2 files changed, 10 insertions(+), 1 deletion(-) + +diff --git a/gfx/layers/opengl/CompositorOGL.cpp b/gfx/layers/opengl/CompositorOGL.cpp +index ed16534208..9b7f244915 100644 +--- a/gfx/layers/opengl/CompositorOGL.cpp ++++ b/gfx/layers/opengl/CompositorOGL.cpp +@@ -2347,8 +2347,15 @@ CompositorOGL::JihadBeginBandPass(int aWidth, int aRows) + GLuint& rb = tall ? mJihadBandTallRB : mJihadBandRB; + GLuint& fbo = tall ? mJihadBandTallFBO : mJihadBandFBO; + int& w = tall ? mJihadBandTallW : mJihadBandW; ++ // a surface that could not be made at this width is not made again every band frame (a line each, ++ // ~25 a second, into the device's small /var): the frame's own surface does the pass ++ int& failW = mJihadBandFailW[tall ? 1 : 0]; ++ if (failW == aWidth) { ++ return 0; ++ } + GLContextEGL* egl = static_cast(mGLContext.get()); + if (!egl->JihadUseBandPBuffer(aWidth, rows, tall)) { ++ failW = aWidth; + return 0; + } + if (!fbo || w != aWidth) { +@@ -2361,8 +2368,9 @@ CompositorOGL::JihadBeginBandPass(int aWidth, int aRows) + mGLContext->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_RENDERBUFFER, rb); + const GLenum st = mGLContext->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); + if (st != LOCAL_GL_FRAMEBUFFER_COMPLETE) { +- fprintf(stderr, "[jihad-gpu] band target %dx%d incomplete (0x%x)\n", aWidth, rows, st); ++ fprintf(stderr, "[jihad-gpu] band target %dx%d incomplete (0x%x): not tried again at this width\n", aWidth, rows, st); + egl->JihadUseFramePBuffer(); ++ failW = aWidth; + return 0; + } + w = aWidth; +diff --git a/gfx/layers/opengl/CompositorOGL.h b/gfx/layers/opengl/CompositorOGL.h +index 0c27bb890a..39968a8791 100644 +--- a/gfx/layers/opengl/CompositorOGL.h ++++ b/gfx/layers/opengl/CompositorOGL.h +@@ -258,6 +258,7 @@ public: + int mJihadBandW = 0; + GLuint mJihadBandTallRB = 0, mJihadBandTallFBO = 0; // T-527: the tall band surface's target + int mJihadBandTallW = 0; ++ int mJihadBandFailW[2] = { 0, 0 }; // T-527: the width a band surface (0) or the tall one (1) failed at: not tried again + // the band surface's rows: JIHAD_GPU_BAND_ROWS (64..942, default 256; round 137) + static int JihadBandRows() { + static const int sRows = [] { diff --git a/build/engine/engine-version.env b/build/engine/engine-version.env index 019f87d..3000755 100644 --- a/build/engine/engine-version.env +++ b/build/engine/engine-version.env @@ -9,7 +9,7 @@ UXP_BASE=5f290d1097a8350b56ce2092aa9d5fc9147c6661 # The tip of our engine branch (NotAlexNoyle/UXP-Jihad, jihad/uxp-: 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=bda3141209f1e97571e6e0879d85c51bb1386df1 +UXP_PIN=f4fefca0d2177c706cfbee2c1d5d520be1896228 # What the user agent and the start pages say: Goanna version and the short UPSTREAM base. GOANNA_VERSION=6.9 UXP_SHORT=5f290d1 diff --git a/build/webos-oe/recipes-jihad/goanna/goanna_1.0.bb b/build/webos-oe/recipes-jihad/goanna/goanna_1.0.bb index f6530fa..39a0f04 100644 --- a/build/webos-oe/recipes-jihad/goanna/goanna_1.0.bb +++ b/build/webos-oe/recipes-jihad/goanna/goanna_1.0.bb @@ -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=" SRC_URI = "git://${JIHAD_REPO}/third_party/uxp;protocol=file;nobranch=1" -SRCREV = "bda3141209f1e97571e6e0879d85c51bb1386df1" +SRCREV = "f4fefca0d2177c706cfbee2c1d5d520be1896228" S = "${WORKDIR}/git" # Modern C++14 toolchain (device-build R1) — the stock dylan/device gcc cannot build UXP. diff --git a/docs/ENGINE-SOURCE.md b/docs/ENGINE-SOURCE.md index 55cb77c..16fb5ab 100644 --- a/docs/ENGINE-SOURCE.md +++ b/docs/ENGINE-SOURCE.md @@ -86,6 +86,8 @@ modifications; the same changes are the commits of `jihad/uxp-RB_20261006`, one | `0061-security-a-certificate-error-is-not-TLS-intolerance-.patch` | `security/manager/ssl/nsNSSIOLayer.cpp` | T-534: a SEC_ERROR_* / mozilla::pkix / bad-cert-domain failure is never retried at a lower TLS version (the retry tripped a TLS 1.3 server's downgrade sentinel and a revoked certificate read "SSL_ERROR_RX_MALFORMED_SERVER_HELLO") | | `0062-layout-the-GPU-path-s-root-displayport-base-is-the-d.patch` | `layout/base/nsLayoutUtils.{h,cpp}`, `layout/generic/nsGfxScrollFrame.cpp` | T-527: on the GPU path the root's displayport base set by the daemon (0059's `jihad_layout_set_displayport2`) is kept by the paint (`nsLayoutUtils::JihadKeepsDisplayPortBase`): `nsLayoutUtils::PaintFrame`'s `DecideScrollableLayer` replaced it on every paint with the composition size at the origin in CSS px, and a zoomed-in card's first paint without margins covered 660 of the 531..861 CSS px shown (`docs/rounds/t527-2026-10-25/notes.txt`) | | `0063-gpu-compositor-a-tall-band-surface-for-a-change-that.patch` | `gfx/gl/GLContextEGL.h`, `gfx/gl/GLContextProviderEGL.cpp`, `gfx/layers/ipc/CompositorBridgeParent.cpp`, `gfx/layers/opengl/CompositorOGL.{h,cpp}` | T-527 (R8): a change that does not fit the 256-row band surface takes a second, 512-row band surface (`JIHAD_GPU_BAND_TALL`, its own pbuffer and target) instead of the frame-sized target; frames are packed for the band surface first. The 480 px WebGL cube's job 15-30 ms where it took ~45 | +| `0064-widget-a-GPU-path-layer-paint-with-nothing-pending-i.patch` | `widget/PuppetWidget.{h,cpp}`, `layout/base/nsLayoutUtils.cpp` | T-527: the daemon's layer paint returns at once after the first when nothing is pending (no invalid region, no paint task, no view-manager flush owed): the display list is not built again for nothing (`JIHAD_GPU_PAINT_ALWAYS=1`: every time); a moved displayport base schedules the root's paint (the margins' comparison saw no change). Desktop Wikipedia's pan 23.6 fps and the long page's 33.1, verify 0 rows wrong: the compositor thread stays the bound | +| `0065-gpu-compositor-a-band-surface-that-cannot-be-made-at.patch` | `gfx/layers/opengl/CompositorOGL.{h,cpp}` | T-527 (review): a band surface or its target that cannot be made at a width is not made again every band frame (a line each into /var); the frame's own surface does the pass | | `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 | diff --git a/third_party/uxp b/third_party/uxp index bda3141..f4fefca 160000 --- a/third_party/uxp +++ b/third_party/uxp @@ -1 +1 @@ -Subproject commit bda3141209f1e97571e6e0879d85c51bb1386df1 +Subproject commit f4fefca0d2177c706cfbee2c1d5d520be1896228