feat: GPU shared texture offscreen rendering (#42953)
* feat: GPU shared texture offscreen rendering * docs: clarify texture infos that passed by the paint event. * feat: make gpu osr spec test optional * fix: osr image compare * fix: remove duplicate test * fix: update patch file * fix: code review * feat: expose more metadata * feat: use better switch design * feat: add warning when user forget to release the texture. * fix: typo * chore: update patch * fix: update patch * fix: update patch description * fix: update docs * fix: apply suggestions from code review Co-authored-by: Charles Kerr <charles@charleskerr.com> * fix: apply suggested fixes --------- Co-authored-by: Charles Kerr <charles@charleskerr.com>
This commit is contained in:
parent
b481966f02
commit
1aeca6fd0e
34 changed files with 1009 additions and 102 deletions
|
@ -15,6 +15,7 @@ import { HexColors, hasCapturableScreen, ScreenCapture } from './lib/screen-help
|
|||
import { once } from 'node:events';
|
||||
import { setTimeout } from 'node:timers/promises';
|
||||
import { setTimeout as syncSetTimeout } from 'node:timers';
|
||||
import { nativeImage } from 'electron';
|
||||
|
||||
const fixtures = path.resolve(__dirname, 'fixtures');
|
||||
const mainFixtures = path.resolve(__dirname, 'fixtures');
|
||||
|
@ -374,7 +375,7 @@ describe('BrowserWindow module', () => {
|
|||
it('should emit did-fail-load event for files that do not exist', async () => {
|
||||
const didFailLoad = once(w.webContents, 'did-fail-load');
|
||||
w.loadURL('file://a.txt');
|
||||
const [, code, desc,, isMainFrame] = await didFailLoad;
|
||||
const [, code, desc, , isMainFrame] = await didFailLoad;
|
||||
expect(code).to.equal(-6);
|
||||
expect(desc).to.equal('ERR_FILE_NOT_FOUND');
|
||||
expect(isMainFrame).to.equal(true);
|
||||
|
@ -382,7 +383,7 @@ describe('BrowserWindow module', () => {
|
|||
it('should emit did-fail-load event for invalid URL', async () => {
|
||||
const didFailLoad = once(w.webContents, 'did-fail-load');
|
||||
w.loadURL('http://example:port');
|
||||
const [, code, desc,, isMainFrame] = await didFailLoad;
|
||||
const [, code, desc, , isMainFrame] = await didFailLoad;
|
||||
expect(desc).to.equal('ERR_INVALID_URL');
|
||||
expect(code).to.equal(-300);
|
||||
expect(isMainFrame).to.equal(true);
|
||||
|
@ -399,7 +400,7 @@ describe('BrowserWindow module', () => {
|
|||
it('should set `mainFrame = false` on did-fail-load events in iframes', async () => {
|
||||
const didFailLoad = once(w.webContents, 'did-fail-load');
|
||||
w.loadFile(path.join(fixtures, 'api', 'did-fail-load-iframe.html'));
|
||||
const [,,,, isMainFrame] = await didFailLoad;
|
||||
const [, , , , isMainFrame] = await didFailLoad;
|
||||
expect(isMainFrame).to.equal(false);
|
||||
});
|
||||
it('does not crash in did-fail-provisional-load handler', (done) => {
|
||||
|
@ -413,7 +414,7 @@ describe('BrowserWindow module', () => {
|
|||
const data = Buffer.alloc(2 * 1024 * 1024).toString('base64');
|
||||
const didFailLoad = once(w.webContents, 'did-fail-load');
|
||||
w.loadURL(`data:image/png;base64,${data}`);
|
||||
const [, code, desc,, isMainFrame] = await didFailLoad;
|
||||
const [, code, desc, , isMainFrame] = await didFailLoad;
|
||||
expect(desc).to.equal('ERR_INVALID_URL');
|
||||
expect(code).to.equal(-300);
|
||||
expect(isMainFrame).to.equal(true);
|
||||
|
@ -4542,7 +4543,7 @@ describe('BrowserWindow module', () => {
|
|||
fs.unlinkSync(savePageHtmlPath);
|
||||
fs.rmdirSync(path.join(savePageDir, 'save_page_files'));
|
||||
fs.rmdirSync(savePageDir);
|
||||
} catch {}
|
||||
} catch { }
|
||||
});
|
||||
|
||||
it('should throw when passing relative paths', async () => {
|
||||
|
@ -4590,7 +4591,7 @@ describe('BrowserWindow module', () => {
|
|||
try {
|
||||
await fs.promises.unlink(savePageMHTMLPath);
|
||||
await fs.promises.rmdir(tmpDir);
|
||||
} catch {}
|
||||
} catch { }
|
||||
});
|
||||
|
||||
it('should save page to disk with HTMLComplete', async () => {
|
||||
|
@ -6367,7 +6368,7 @@ describe('BrowserWindow module', () => {
|
|||
it('creates offscreen window with correct size', async () => {
|
||||
const paint = once(w.webContents, 'paint') as Promise<[any, Electron.Rectangle, Electron.NativeImage]>;
|
||||
w.loadFile(path.join(fixtures, 'api', 'offscreen-rendering.html'));
|
||||
const [,, data] = await paint;
|
||||
const [, , data] = await paint;
|
||||
expect(data.constructor.name).to.equal('NativeImage');
|
||||
expect(data.isEmpty()).to.be.false('data is empty');
|
||||
const size = data.getSize();
|
||||
|
@ -6465,6 +6466,52 @@ describe('BrowserWindow module', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('offscreen rendering image', () => {
|
||||
afterEach(closeAllWindows);
|
||||
|
||||
const imagePath = path.join(fixtures, 'assets', 'osr.png');
|
||||
const targetImage = nativeImage.createFromPath(imagePath);
|
||||
const nativeModulesEnabled = !process.env.ELECTRON_SKIP_NATIVE_MODULE_TESTS;
|
||||
ifit(nativeModulesEnabled && ['win32'].includes(process.platform))('use shared texture, hardware acceleration enabled', (done) => {
|
||||
const { ExtractPixels, InitializeGpu } = require('@electron-ci/osr-gpu');
|
||||
|
||||
try {
|
||||
InitializeGpu();
|
||||
} catch (e) {
|
||||
console.log('Failed to initialize GPU, this spec needs a valid GPU device. Skipping...');
|
||||
console.error(e);
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
offscreen: {
|
||||
useSharedTexture: true
|
||||
}
|
||||
},
|
||||
transparent: true,
|
||||
frame: false,
|
||||
width: 128,
|
||||
height: 128
|
||||
});
|
||||
|
||||
w.webContents.once('paint', async (e, dirtyRect) => {
|
||||
try {
|
||||
expect(e.texture).to.be.not.null();
|
||||
const pixels = ExtractPixels(e.texture!.textureInfo);
|
||||
const img = nativeImage.createFromBitmap(pixels, { width: dirtyRect.width, height: dirtyRect.height, scaleFactor: 1 });
|
||||
expect(img.toBitmap().equals(targetImage.toBitmap())).to.equal(true);
|
||||
done();
|
||||
} catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
w.loadFile(imagePath);
|
||||
});
|
||||
});
|
||||
|
||||
describe('"transparent" option', () => {
|
||||
afterEach(closeAllWindows);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue