Read many pixels from one render in the e2e checks (#283)
On the WSL runner every __gm.pixelAt costs about 7 ms, three times as much as on stein2. Each call renders the whole stage into a fresh render texture and stalls until the GPU hands one pixel back. pixelRow and several checks read hundreds of pixels that way. Module 05 spent 36.8 of its 40 s on these reads, module 04 11.3 of 20 s. - stagePixels in src/ui/render.ts renders once around all points and reads them in one go. Only whole pixels at resolution 1 take that path. Anything else stays point by point. - The hook __gm.pixels(points) exposes it, and pixelRow uses it. - The loops in e2e 04, 05, 14 and 44 read their grids in one call. - The old comment in pixelRow said a wider frame comes back resampled. That is not true. The colours match exactly, so the comment is gone. Measured on the RTX (d3d12) and on stein2 (Intel, Vulkan): diff 0 at 10 680 points on each GPU. One screen row takes 4 to 18 ms instead of 2.4 to 9.9 s. The six affected modules need 32 s instead of 139 s under WSL, faster than on stein2. The rest of the gap is in Chromium's GPU process. perf puts three quarters of its time in the drivers (NVIDIA D3D12, Mesa d3d12, dxg). Four browser and Mesa settings were measured, none helped. Tests: - ./check.sh on melektaus-atx: 195 files, 2827 tests, CHECK EXIT 0 (+4) - e2e 1-16,30,37,44,58 under WSL: ALL PASSED, 587 checks - counter-check: a wrong index in stagePixels fails the unit test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
857e23c69b
commit
ffedd81803
8 files changed
+132
-36
No files matched your search
@@ -1,6 +1,6 @@
|
||||
import { type FrameRect, isTextureFile, resolvePicture } from '../../core/model/texture';
|
||||
import { effectiveScale, gameClock } from '../../core/time';
|
||||
import { afterFrames, isRendering, renderCount, requestRender, stagePixel } from '../../ui';
|
||||
import { afterFrames, isRendering, renderCount, requestRender, stagePixel, stagePixels } from '../../ui';
|
||||
import { particleLibrary } from '../../viewport/ParticleLibrary';
|
||||
import { shaderLibrary } from '../../viewport/ShaderLibrary';
|
||||
import { textureLibrary } from '../../viewport/TextureLibrary';
|
||||
@@ -13,6 +13,8 @@ export function renderHooks({ app, state, editor }: HookContext) {
|
||||
const pixelAt = (x: number, y: number): string => stagePixel(app, x, y);
|
||||
return {
|
||||
pixelAt,
|
||||
/** Many points from one render, each the colour `pixelAt` would give – for checks that sample hundreds. */
|
||||
pixels: (points: readonly { x: number; y: number }[]) => stagePixels(app, points),
|
||||
/**
|
||||
* A horizontal run of pixels in one read. A check that has to tell a repeated pattern from a
|
||||
* stretched one needs dozens of samples, and dozens of round trips would cost more than the
|
||||
@@ -20,11 +22,10 @@ export function renderHooks({ app, state, editor }: HookContext) {
|
||||
*/
|
||||
pixelRow: (x: number, y: number, count: number) => {
|
||||
const width = Math.max(1, Math.min(1024, Math.round(count)));
|
||||
const row: string[] = [];
|
||||
// One extract per pixel, not one strip: a wider frame comes back resampled, and a row that
|
||||
// blends its neighbours cannot tell a hard border from a soft one – which is what it is for.
|
||||
for (let i = 0; i < width; i++) row.push(pixelAt(x + i, y));
|
||||
return row;
|
||||
return stagePixels(
|
||||
app,
|
||||
Array.from({ length: width }, (_, i) => ({ x: x + i, y })),
|
||||
);
|
||||
},
|
||||
/** Every uploaded picture the project names and whether it is in memory, drawn anywhere or not. */
|
||||
heldPictures: () =>
|
||||
|
||||
Reference in new issue
Block a user