The 480 px spinning cube, the sensors running: 25.3-28.0 fps on the card (was 16.6-22.0), 360 px 28.9-29.9, 240 px 29.4-30.0 (the page's own ~30); verify with a ticker beside the canvas and the cube stopped 0 rows wrong. - The frame trailer's dirty words (render/browserserver/Src/JihadFrameWindow.h): before each msgPainted the daemon writes the rect that differs from the frame published before it and both frames' numbers — the union of every composite's change since, at that frame's geometry; a forgotten job, popups, a software frame or an earlier-posted job not yet polled make it whole (BrowserPageGoanna pubDirtyAdd/pubDirtyTake, publishWindow; JIHAD_GPU_NO_DIRTY=1 off). The adapter, showing that base frame at the same geometry, zoom and scroll, invalidates only the rect (BrowserAdapter::msgPainted, BrowserOffscreen::dirtyRect). The YAP messages and the header are unchanged; docs/IPC-CONTRACT.md now describes the trailer. - Engine patch 0063 (bda31412): a change too tall for the 256-row band surface takes a 512-row band surface (JIHAD_GPU_BAND_TALL) instead of the frame-sized target; frames are packed for the band surface first, so desktop Wikipedia's pan frames stay where they were (its pan unchanged). Pins moved; 63 patches reproduce. - The PDK adapter had not built since T-540: YapProxy.h's member initializer (m_sendFailed) is C++11 and the PDK's gcc 4.3.3 refuses it; initialized in the constructor. The YAP contract check passes. - The pan's 56 ms+ gaps traced: tools/host/gap-trace.py, and with JIHAD_GPU_TIMELINE=1 the express lane notes why it made no frame and every trailer is logged. Desktop Wikipedia's 10 s pan: 24 gaps, all the compositor job's (a whole read after a tree change, the copy from the base, a job behind the page's tile uploads). - default-check.sh reads across the daemon log's 2 MB rotation; gpu-spike-collect.sh removes the frame dumps. - Kit R8's 24 fps criterion ticked, R3's gap criterion annotated; T-511 DONE; notes, README knobs, ENGINE-SOURCE 0063, PICKUP. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
268 lines
8.7 KiB
C++
268 lines
8.7 KiB
C++
/* @@@LICENSE
|
|
*
|
|
* Copyright (c) 2012 Hewlett-Packard Development Company, L.P.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
LICENSE@@@ */
|
|
|
|
/* Modified by the Jihad Browser project (2026), per Apache-2.0 section 4(b): this file differs from
|
|
* HP's isis-project original. The changes are this repository's git history for this file. */
|
|
|
|
#include <math.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <linux/fb.h>
|
|
#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>
|
|
|
|
#include "BrowserOffscreen.h"
|
|
#include "BrowserOffscreenCalculations.h"
|
|
#include "BrowserRect.h"
|
|
|
|
static const float kOffscreenSizeAsScreenSizeMultiplier = 4.0f;
|
|
|
|
// Used only for desktop builds
|
|
static const int kDefaultScreenWidth = 1024;
|
|
static const int kDefaultScreenHeight = 768;
|
|
|
|
static double kDoubleZeroTolerance = 0.0001;
|
|
|
|
static bool PrvGetScreenDimensions(int& width, int& height)
|
|
{
|
|
#if defined(TARGET_DESKTOP)
|
|
width = 1024;
|
|
height = 768;
|
|
return true;
|
|
#endif
|
|
|
|
int fd = ::open("/dev/fb0", O_RDONLY);
|
|
if (fd < 0)
|
|
return false;
|
|
|
|
struct fb_var_screeninfo varinfo;
|
|
::memset(&varinfo, 0, sizeof(varinfo));
|
|
|
|
if (::ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) == -1) {
|
|
::close(fd);
|
|
return false;
|
|
}
|
|
|
|
width = varinfo.xres;
|
|
height = varinfo.yres;
|
|
::close(fd);
|
|
|
|
return true;
|
|
}
|
|
|
|
static inline bool PrvIsEqual(double a, double b)
|
|
{
|
|
return (fabs(a-b) < kDoubleZeroTolerance);
|
|
}
|
|
|
|
BrowserOffscreen* BrowserOffscreen::create()
|
|
{
|
|
int screenWidth, screenHeight;
|
|
if (!PrvGetScreenDimensions(screenWidth, screenHeight)) {
|
|
screenWidth = kDefaultScreenWidth;
|
|
screenHeight = kDefaultScreenHeight;
|
|
}
|
|
|
|
int bufferSize = screenWidth *
|
|
screenHeight *
|
|
sizeof(unsigned int) *
|
|
kOffscreenSizeAsScreenSizeMultiplier;
|
|
|
|
IpcBuffer* buffer = IpcBuffer::create(bufferSize + sizeof(BrowserOffscreenInfo));
|
|
if (!buffer) {
|
|
return 0;
|
|
}
|
|
// T-517: this adapter reads frame windows (JihadFrameWindow.h); the daemon moves them only then
|
|
if (JihadFrameTrailer* t = jihadFrameTrailer((unsigned char*)buffer->buffer(), buffer->size())) {
|
|
::memset(t, 0, sizeof(*t));
|
|
t->adapterCaps = kJihadWindowCaps;
|
|
}
|
|
return new BrowserOffscreen(buffer);
|
|
}
|
|
|
|
uint32_t BrowserOffscreen::frameSeq() const
|
|
{
|
|
const JihadFrameTrailer* t = jihadFrameTrailer((unsigned char*)m_ipcBuffer->buffer(), m_ipcBuffer->size());
|
|
return (t && t->magic == kJihadWindowMagic) ? t->dirtySeq : 0;
|
|
}
|
|
|
|
bool BrowserOffscreen::dirtyRect(uint32_t* base, int* left, int* top, int* right, int* bottom) const
|
|
{
|
|
const JihadFrameTrailer* t = jihadFrameTrailer((unsigned char*)m_ipcBuffer->buffer(), m_ipcBuffer->size());
|
|
if (!t || t->magic != kJihadWindowMagic || t->dirtyMagic != kJihadDirtyMagic)
|
|
return false;
|
|
*base = t->dirtyBase;
|
|
*left = t->dirtyLeft; *top = t->dirtyTop; *right = t->dirtyRight; *bottom = t->dirtyBottom;
|
|
return true;
|
|
}
|
|
|
|
BrowserOffscreen* BrowserOffscreen::attach(int key, int size)
|
|
{
|
|
IpcBuffer* buffer = IpcBuffer::attach(key, size);
|
|
if (!buffer)
|
|
return 0;
|
|
|
|
return new BrowserOffscreen(buffer);
|
|
}
|
|
|
|
BrowserOffscreen::BrowserOffscreen(IpcBuffer* ipcBuffer)
|
|
: m_ipcBuffer(ipcBuffer)
|
|
, m_buffer((unsigned char*)ipcBuffer->buffer() + sizeof(BrowserOffscreenInfo))
|
|
, m_header((BrowserOffscreenInfo*)m_ipcBuffer->buffer())
|
|
{
|
|
resetBuffer();
|
|
}
|
|
|
|
BrowserOffscreen::~BrowserOffscreen()
|
|
{
|
|
delete m_ipcBuffer;
|
|
}
|
|
|
|
void BrowserOffscreen::clear()
|
|
{
|
|
::memset(m_buffer, 0xFF, rasterSize());
|
|
}
|
|
|
|
void BrowserOffscreen::copyFrom(BrowserOffscreen* other, BrowserRect* r)
|
|
{
|
|
// Check if we can actually copy from this buffer
|
|
if (m_header->bufferWidth != other->m_header->bufferWidth ||
|
|
m_header->bufferHeight != other->m_header->bufferHeight ||
|
|
!PrvIsEqual(m_header->contentZoom, other->m_header->contentZoom))
|
|
return;
|
|
|
|
BrowserRect myRect(m_header->renderedX,
|
|
m_header->renderedY,
|
|
m_header->renderedWidth,
|
|
m_header->renderedHeight);
|
|
|
|
BrowserRect otherRect(other->m_header->renderedX,
|
|
other->m_header->renderedY,
|
|
other->m_header->renderedWidth,
|
|
other->m_header->renderedHeight);
|
|
|
|
if (!myRect.intersects(otherRect))
|
|
return;
|
|
|
|
myRect.intersect(otherRect);
|
|
if (r) {
|
|
if (!myRect.intersects(*r)) return;
|
|
myRect.intersect(*r);
|
|
}
|
|
|
|
unsigned int* src = (unsigned int*) other->rasterBuffer();
|
|
unsigned int* dst = (unsigned int*) rasterBuffer();
|
|
int stride = m_header->renderedWidth;
|
|
|
|
src += (myRect.y() - other->m_header->renderedY) * stride +
|
|
(myRect.x() - other->m_header->renderedX);
|
|
dst += (myRect.y() - m_header->renderedY) * stride +
|
|
(myRect.x() - m_header->renderedX);
|
|
|
|
for (int j = 0; j < myRect.h(); j++) {
|
|
::memcpy(dst, src, myRect.w() * sizeof(unsigned int));
|
|
src += stride;
|
|
dst += stride;
|
|
}
|
|
}
|
|
|
|
QImage BrowserOffscreen::surface()
|
|
{
|
|
QImage result;
|
|
|
|
if ((m_header->renderedWidth > 0) && (m_header->renderedHeight > 0))
|
|
result = QImage(rasterBuffer(), m_header->renderedWidth, m_header->renderedHeight, QImage::Format_ARGB32_Premultiplied);
|
|
|
|
return result;
|
|
}
|
|
|
|
// T-517: the frame's window, when the daemon wrote one that fits the pixels (else the raster's start)
|
|
size_t BrowserOffscreen::windowBytes() const
|
|
{
|
|
JihadFrameTrailer* t = jihadFrameTrailer((unsigned char*)m_ipcBuffer->buffer(), m_ipcBuffer->size());
|
|
if (!t || t->magic != kJihadWindowMagic)
|
|
return 0;
|
|
const int rows = t->windowRows; // read once: the checks and the offset are of the same value
|
|
if (rows <= 0 || m_header->renderedWidth <= 0 || m_header->renderedHeight <= 0)
|
|
return 0;
|
|
// 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)((uint64_t)rows * rowBytes); // <= rasterSize()
|
|
}
|
|
|
|
void BrowserOffscreen::resetBuffer()
|
|
{
|
|
::memset(m_header, 0, sizeof(BrowserOffscreenInfo));
|
|
}
|
|
|
|
bool BrowserOffscreen::matchesParams(BrowserOffscreenCalculations* calc) const
|
|
{
|
|
return
|
|
m_header->bufferWidth == calc->bufferWidth &&
|
|
m_header->bufferHeight == calc->bufferHeight &&
|
|
m_header->renderedX == calc->renderX &&
|
|
m_header->renderedY == calc->renderY &&
|
|
m_header->renderedWidth == calc->renderWidth &&
|
|
m_header->renderedHeight == calc->renderHeight &&
|
|
PrvIsEqual(m_header->contentZoom, calc->contentZoom);
|
|
}
|
|
|
|
void BrowserOffscreen::updateParams(BrowserOffscreenCalculations* calc)
|
|
{
|
|
resetBuffer();
|
|
|
|
m_header->bufferWidth = calc->bufferWidth;
|
|
m_header->bufferHeight = calc->bufferHeight;
|
|
|
|
m_header->contentZoom = calc->contentZoom;
|
|
|
|
m_header->renderedX = calc->renderX;
|
|
m_header->renderedY = calc->renderY;
|
|
m_header->renderedWidth = calc->renderWidth;
|
|
m_header->renderedHeight = calc->renderHeight;
|
|
}
|
|
|
|
bool BrowserOffscreen::matchesParams(BrowserOffscreen* other) const
|
|
{
|
|
return
|
|
m_header->bufferWidth == other->m_header->bufferWidth &&
|
|
m_header->bufferHeight == other->m_header->bufferHeight &&
|
|
m_header->renderedX == other->m_header->renderedX &&
|
|
m_header->renderedY == other->m_header->renderedY &&
|
|
m_header->renderedWidth == other->m_header->renderedWidth &&
|
|
m_header->renderedHeight == other->m_header->renderedHeight &&
|
|
PrvIsEqual(m_header->contentZoom, other->m_header->contentZoom);
|
|
}
|
|
|
|
int BrowserOffscreen::rasterSize() const
|
|
{
|
|
return m_ipcBuffer->size() - sizeof(BrowserOffscreenInfo) - kJihadTrailerBytes; // T-517: the trailer after it
|
|
}
|
|
|