Use faster pixel check method with caching

This commit is contained in:
Daniel Imms
2023-09-08 10:12:06 -07:00
parent 070d194c6c
commit 413e884efb
3 changed files with 42 additions and 29 deletions
@@ -4,9 +4,10 @@
*/ */
import { assert } from 'chai'; import { assert } from 'chai';
import test from '@playwright/test'; import test, { LocatorScreenshotOptions } from '@playwright/test';
import { ITheme } from 'xterm'; import { ITheme } from 'xterm';
import { ITestContext, createTestContext, openTerminal, pollFor } from '../../../out-test/playwright/TestUtils'; import { ITestContext, MaybeAsync, createTestContext, openTerminal, pollFor } from '../../../out-test/playwright/TestUtils';
import { IImage32, decodePng } from '@lunapaint/png-codec';
let ctx: ITestContext; let ctx: ITestContext;
test.beforeAll(async ({ browser }) => { test.beforeAll(async ({ browser }) => {
@@ -28,7 +29,7 @@ test.describe.only('WebGL Renderer Integration Tests', async () => {
`); `);
// TODO: Introduce frame caching // TODO: Introduce frame caching
// Clear the cached screenshot before each test // Clear the cached screenshot before each test
// frameDetails = undefined; frameDetails = undefined;
}); });
test('dispose removes renderer canvases', async function(): Promise<void> { test('dispose removes renderer canvases', async function(): Promise<void> {
@@ -792,6 +793,7 @@ test.describe.only('WebGL Renderer Integration Tests', async () => {
// exact to the contrast ratio, if the increase luminance algorithm // exact to the contrast ratio, if the increase luminance algorithm
// changes then these will probably fail // changes then these will probably fail
await ctx.page.evaluate(`window.term.options.minimumContrastRatio = 10;`); await ctx.page.evaluate(`window.term.options.minimumContrastRatio = 10;`);
frameDetails = undefined;
await pollFor(ctx.page, () => getCellColor(1, 1), [176, 180, 180, 255]); await pollFor(ctx.page, () => getCellColor(1, 1), [176, 180, 180, 255]);
await pollFor(ctx.page, () => getCellColor(2, 1), [238, 158, 158, 255]); await pollFor(ctx.page, () => getCellColor(2, 1), [238, 158, 158, 255]);
await pollFor(ctx.page, () => getCellColor(3, 1), [152, 198, 110, 255]); await pollFor(ctx.page, () => getCellColor(3, 1), [152, 198, 110, 255]);
@@ -861,6 +863,7 @@ test.describe.only('WebGL Renderer Integration Tests', async () => {
// exact to the contrast ratio, if the increase luminance algorithm // exact to the contrast ratio, if the increase luminance algorithm
// changes then these will probably fail // changes then these will probably fail
await ctx.page.evaluate(`window.term.options.minimumContrastRatio = 10;`); await ctx.page.evaluate(`window.term.options.minimumContrastRatio = 10;`);
frameDetails = undefined;
await pollFor(ctx.page, () => getCellColor(1, 1), [46, 52, 54, 255]); await pollFor(ctx.page, () => getCellColor(1, 1), [46, 52, 54, 255]);
await pollFor(ctx.page, () => getCellColor(2, 1), [132, 0, 0, 255]); await pollFor(ctx.page, () => getCellColor(2, 1), [132, 0, 0, 255]);
await pollFor(ctx.page, () => getCellColor(3, 1), [36, 72, 0, 255]); await pollFor(ctx.page, () => getCellColor(3, 1), [36, 72, 0, 255]);
@@ -935,6 +938,7 @@ test.describe.only('WebGL Renderer Integration Tests', async () => {
// exact to the contrast ratio, if the increase luminance algorithm // exact to the contrast ratio, if the increase luminance algorithm
// changes then these will probably fail // changes then these will probably fail
await ctx.page.evaluate(`window.term.options.minimumContrastRatio = 10;`); await ctx.page.evaluate(`window.term.options.minimumContrastRatio = 10;`);
frameDetails = undefined;
await pollFor(ctx.page, () => getCellColor(1, 1), [150, 153, 154, 255]); await pollFor(ctx.page, () => getCellColor(1, 1), [150, 153, 154, 255]);
await pollFor(ctx.page, () => getCellColor(2, 1), [229, 127, 127, 255]); await pollFor(ctx.page, () => getCellColor(2, 1), [229, 127, 127, 255]);
await pollFor(ctx.page, () => getCellColor(3, 1), [63, 124, 4, 255]); await pollFor(ctx.page, () => getCellColor(3, 1), [63, 124, 4, 255]);
@@ -967,6 +971,7 @@ test.describe.only('WebGL Renderer Integration Tests', async () => {
await pollFor(ctx.page, () => getCellColor(2, 1), [255, 0, 0, 255]); await pollFor(ctx.page, () => getCellColor(2, 1), [255, 0, 0, 255]);
await pollFor(ctx.page, () => getCellColor(3, 1), [0, 255, 0, 255]); await pollFor(ctx.page, () => getCellColor(3, 1), [0, 255, 0, 255]);
await ctx.page.evaluate(`window.term.selectAll()`); await ctx.page.evaluate(`window.term.selectAll()`);
frameDetails = undefined;
// Selection only cell needs to be first to ensure renderer has kicked in // Selection only cell needs to be first to ensure renderer has kicked in
await pollFor(ctx.page, () => getCellColor(1, 1), [0, 0, 255, 255]); await pollFor(ctx.page, () => getCellColor(1, 1), [0, 0, 255, 255]);
await pollFor(ctx.page, () => getCellColor(2, 1), [255, 0, 0, 255]); await pollFor(ctx.page, () => getCellColor(2, 1), [255, 0, 0, 255]);
@@ -1086,33 +1091,40 @@ test.describe.only('WebGL Renderer Integration Tests', async () => {
}); });
}); });
async function getCellColor(col: number, row: number): Promise<number[]> { /**
await ctx.page.evaluate(` * Gets the color of the pixel in the center of a cell.
window.gl = window.term._core._renderService._renderer.value._gl; * @param col The 1-based column index to get the color for.
window.result = new Uint8Array(4); * @param row The 1-based row index to get the color for.
window.d = window.term._core._renderService.dimensions; */
window.gl.readPixels( function getCellColor(col: number, row: number): MaybeAsync<[red: number, green: number, blue: number, alpha: number]> {
Math.floor((${col - 0.5}) * window.d.device.cell.width), if (!frameDetails) {
Math.floor(window.gl.drawingBufferHeight - 1 - (${row - 0.5}) * window.d.device.cell.height), return getFrameDetails().then(frameDetails => getCellColorInner(frameDetails, col, row));
1, 1, window.gl.RGBA, window.gl.UNSIGNED_BYTE, window.result }
); return getCellColorInner(frameDetails, col, row);
`);
return await ctx.page.evaluate(`Array.from(window.result)`);
} }
// async function getCellPixels(col: number, row: number): Promise<number[]> { let frameDetails: { cols: number, rows: number, decoded: IImage32 } | undefined = undefined;
// await ctx.page.evaluate(` async function getFrameDetails(): Promise<{ cols: number, rows: number, decoded: IImage32 }> {
// window.gl = window.term._core._renderService._renderer.value._gl; const screenshotOptions: LocatorScreenshotOptions | undefined = process.env.DEBUG ? { path: 'out-test/playwright/screenshot.png' } : undefined;
// window.result = new Uint8Array(window.d.device.cell.width * window.d.device.cell.height * 4); const buffer = await ctx.page.locator('#terminal-container .xterm-screen').screenshot(screenshotOptions);
// window.d = window.term._core._renderService.dimensions; frameDetails = {
// window.gl.readPixels( cols: await ctx.proxy.cols,
// Math.floor(${col - 1} * window.d.device.cell.width), rows: await ctx.proxy.rows,
// Math.floor(window.gl.drawingBufferHeight - ${row} * window.d.device.cell.height), decoded: (await decodePng(buffer, { force32: true })).image
// window.d.device.cell.width, window.d.device.cell.height, window.gl.RGBA, window.gl.UNSIGNED_BYTE, window.result };
// ); return frameDetails;
// `); }
// return await ctx.page.evaluate(`Array.from(window.result)`);
// } function getCellColorInner(frameDetails: { cols: number, rows: number, decoded: IImage32 }, col: number, row: number): [red: number, green: number, blue: number, alpha: number] {
const cellSize = {
width: frameDetails.decoded.width / frameDetails.cols,
height: frameDetails.decoded.height / frameDetails.rows
};
const x = Math.floor((col - 1/* 1- to 0-based index */ + 0.5/* middle of cell */) * cellSize.width);
const y = Math.floor((row - 1/* 1- to 0-based index */ + 0.5/* middle of cell */) * cellSize.height);
const i = (y * frameDetails.decoded.width + x) * 4/* 4 channels per pixel */;
return Array.from(frameDetails.decoded.data.slice(i, i + 4)) as [number, number, number, number];
}
const COLORS_16_TO_255 = [ const COLORS_16_TO_255 = [
'#000000', '#00005f', '#000087', '#0000af', '#0000d7', '#0000ff', '#005f00', '#005f5f', '#005f87', '#005faf', '#005fd7', '#005fff', '#008700', '#00875f', '#008787', '#0087af', '#000000', '#00005f', '#000087', '#0000af', '#0000d7', '#0000ff', '#005f00', '#005f5f', '#005f87', '#005faf', '#005fd7', '#005fff', '#008700', '#00875f', '#008787', '#0087af',
@@ -21,6 +21,7 @@
"strict": true, "strict": true,
"types": [ "types": [
"../../../node_modules/@types/node", "../../../node_modules/@types/node",
"../../../node_modules/@lunapaint/png-codec",
"../../../out-test/playwright/TestUtils" "../../../out-test/playwright/TestUtils"
] ]
}, },
+1 -1
View File
@@ -9,7 +9,7 @@ const cp = require('child_process');
const path = require('path'); const path = require('path');
const configs = [ const configs = [
{ name: 'core', path: 'out-test/playwright/playwright.config.js' }, // { name: 'core', path: 'out-test/playwright/playwright.config.js' },
{ name: 'xterm-addon-webgl', path: 'addons/xterm-addon-webgl/out-test/playwright.config.js' } { name: 'xterm-addon-webgl', path: 'addons/xterm-addon-webgl/out-test/playwright.config.js' }
]; ];