Skip to content

Commit 433bfc6

Browse files
committed
feat: GPU shared texture offscreen rendering
1 parent f5fb44e commit 433bfc6

24 files changed

+313
-42
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# OffscreenSharedTexture Object
2+
3+
* `pixelFormat` string - The pixel format of the texture, could be RGBA or BGRA.
4+
* `sharedTextureHandle` string - _macOS_ _Windows_ The handle to the shared texture.
5+
* `planes` Object[] - _Linux_ Each plane's info of the shared texture.
6+
* `stride` number - The strides and offsets in bytes to be used when accessing the buffers via a memory mapping. One per plane per entry.
7+
* `offset` number - The strides and offsets in bytes to be used when accessing the buffers via a memory mapping. One per plane per entry.
8+
* `size` number - Size in bytes of the plane. This is necessary to map the buffers.
9+
* `fd` number - File descriptor for the underlying memory object (usually dmabuf).
10+
* `modifier` string - _Linux_ The modifier is retrieved from GBM library and passed to EGL driver.

docs/api/structures/web-preferences.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@
8383
window. Defaults to `false`. See the
8484
[offscreen rendering tutorial](../../tutorial/offscreen-rendering.md) for
8585
more details.
86+
* `offscreenUseSharedTexture` boolean (optional) - Whether to use GPU shared texture for accelerated
87+
paint event. Defaults to `false`. See the
88+
[offscreen rendering tutorial](../../tutorial/offscreen-rendering.md) for
89+
more details.
8690
* `contextIsolation` boolean (optional) - Whether to run Electron APIs and
8791
the specified `preload` script in a separate JavaScript context. Defaults
8892
to `true`. The context that the `preload` script runs in will only have

docs/api/web-contents.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,7 @@ Returns:
874874
* `event` Event
875875
* `dirtyRect` [Rectangle](structures/rectangle.md)
876876
* `image` [NativeImage](native-image.md) - The image data of the whole frame.
877+
* `texture` [OffscreenSharedTexture](structures/offscreen-shared-texture.md) - The GPU shared texture of the frame. `null` if `webPreferences.offscreenUseSharedTexture` is `false`.
877878

878879
Emitted when a new frame is generated. Only the dirty area is passed in the
879880
buffer.
@@ -882,7 +883,7 @@ buffer.
882883
const { BrowserWindow } = require('electron')
883884

884885
const win = new BrowserWindow({ webPreferences: { offscreen: true } })
885-
win.webContents.on('paint', (event, dirty, image) => {
886+
win.webContents.on('paint', (event, dirty, image, texture) => {
886887
// updateBitmap(dirty, image.getBitmap())
887888
})
888889
win.loadURL('https://github.com')

docs/fiddles/features/offscreen-rendering/main.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,19 @@ function createWindow () {
99
width: 800,
1010
height: 600,
1111
webPreferences: {
12-
offscreen: true
12+
offscreen: true,
13+
offscreenUseSharedTexture: true // or false
1314
}
1415
})
1516

1617
win.loadURL('https://github.com')
17-
win.webContents.on('paint', (event, dirty, image) => {
18-
fs.writeFileSync('ex.png', image.toPNG())
18+
win.webContents.on('paint', (event, dirty, image, texture) => {
19+
if (texture) {
20+
// This will be not null when `offscreenUseSharedTexture` is true.
21+
// Import the shared texture handle to your own rendering world.
22+
} else {
23+
fs.writeFileSync('ex.png', image.toPNG())
24+
}
1925
})
2026
win.webContents.setFrameRate(60)
2127
console.log(`The screenshot has been successfully saved to ${path.join(process.cwd(), 'ex.png')}`)

docs/tutorial/offscreen-rendering.md

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
## Overview
44

55
Offscreen rendering lets you obtain the content of a `BrowserWindow` in a
6-
bitmap, so it can be rendered anywhere, for example, on texture in a 3D scene.
6+
bitmap or a shared GPU texture, so it can be rendered anywhere, for example,
7+
on texture in a 3D scene.
78
The offscreen rendering in Electron uses a similar approach to that of the
89
[Chromium Embedded Framework](https://bitbucket.org/chromiumembedded/cef)
910
project.
@@ -23,10 +24,22 @@ losses with no benefits.
2324

2425
#### GPU accelerated
2526

26-
GPU accelerated rendering means that the GPU is used for composition. Because of
27-
that, the frame has to be copied from the GPU which requires more resources,
28-
thus this mode is slower than the Software output device. The benefit of this
29-
mode is that WebGL and 3D CSS animations are supported.
27+
GPU accelerated rendering means that the GPU is used for composition. The benefit
28+
of this mode is that WebGL and 3D CSS animations are supported. There're two
29+
different approaches depends on whether `webPreferences.offscreenUseSharedTexture`
30+
is set to true.
31+
32+
1. Use shared texture
33+
34+
The frame are directly copied in GPU textures, thus this mode is very fast because
35+
there's no CPU-GPU memory copies overhead and you can directly import the shared
36+
texture to your own rendering program. The texture is passed in `texture` param of
37+
`paint` event.
38+
39+
2. Use shared memory bitmap
40+
41+
The frame has to be copied from the GPU to the CPU bitmap which requires more
42+
resources, thus this mode is slower than the Software output device.
3043

3144
#### Software output device
3245

@@ -51,13 +64,19 @@ function createWindow () {
5164
width: 800,
5265
height: 600,
5366
webPreferences: {
54-
offscreen: true
67+
offscreen: true,
68+
offscreenUseSharedTexture: true // or false
5569
}
5670
})
5771
5872
win.loadURL('https://github.com')
59-
win.webContents.on('paint', (event, dirty, image) => {
60-
fs.writeFileSync('ex.png', image.toPNG())
73+
win.webContents.on('paint', (event, dirty, image, texture) => {
74+
if (texture) {
75+
// This will be not null when `offscreenUseSharedTexture` is true.
76+
// Import the shared texture handle to your own rendering world.
77+
} else {
78+
fs.writeFileSync('ex.png', image.toPNG())
79+
}
6180
})
6281
win.webContents.setFrameRate(60)
6382
console.log(`The screenshot has been successfully saved to ${path.join(process.cwd(), 'ex.png')}`)

filenames.auto.gni

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ auto_filenames = {
108108
"docs/api/structures/mouse-wheel-input-event.md",
109109
"docs/api/structures/notification-action.md",
110110
"docs/api/structures/notification-response.md",
111+
"docs/api/structures/offscreen-shared-texture.md",
111112
"docs/api/structures/open-external-permission-request.md",
112113
"docs/api/structures/payment-discount.md",
113114
"docs/api/structures/permission-request.md",

filenames.gni

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,8 @@ filenames = {
451451
"shell/browser/notifications/platform_notification_service.h",
452452
"shell/browser/osr/osr_host_display_client.cc",
453453
"shell/browser/osr/osr_host_display_client.h",
454+
"shell/browser/osr/osr_paint_event.cc",
455+
"shell/browser/osr/osr_paint_event.h",
454456
"shell/browser/osr/osr_render_widget_host_view.cc",
455457
"shell/browser/osr/osr_render_widget_host_view.h",
456458
"shell/browser/osr/osr_video_consumer.cc",
@@ -597,6 +599,8 @@ filenames = {
597599
"shell/common/gin_converters/net_converter.cc",
598600
"shell/common/gin_converters/net_converter.h",
599601
"shell/common/gin_converters/optional_converter.h",
602+
"shell/common/gin_converters/osr_converter.cc",
603+
"shell/common/gin_converters/osr_converter.h",
600604
"shell/common/gin_converters/serial_port_info_converter.h",
601605
"shell/common/gin_converters/std_converter.h",
602606
"shell/common/gin_converters/time_converter.cc",

shell/browser/api/electron_api_web_contents.cc

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@
119119
#include "shell/common/gin_converters/image_converter.h"
120120
#include "shell/common/gin_converters/net_converter.h"
121121
#include "shell/common/gin_converters/optional_converter.h"
122+
#include "shell/common/gin_converters/osr_converter.h"
122123
#include "shell/common/gin_converters/value_converter.h"
123124
#include "shell/common/gin_helper/dictionary.h"
124125
#include "shell/common/gin_helper/object_template_builder.h"
@@ -753,6 +754,9 @@ WebContents::WebContents(v8::Isolate* isolate,
753754
if (options.Get(options::kOffscreen, &b) && b)
754755
type_ = Type::kOffScreen;
755756

757+
if (options.Get(options::kOffscreenUseSharedTexture, &b))
758+
offscreen_use_shared_texture_ = b;
759+
756760
// Init embedder earlier
757761
options.Get("embedder", &embedder_);
758762

@@ -787,7 +791,7 @@ WebContents::WebContents(v8::Isolate* isolate,
787791

788792
if (embedder_ && embedder_->IsOffScreen()) {
789793
auto* view = new OffScreenWebContentsView(
790-
false,
794+
false, offscreen_use_shared_texture_,
791795
base::BindRepeating(&WebContents::OnPaint, base::Unretained(this)));
792796
params.view = view;
793797
params.delegate_view = view;
@@ -807,7 +811,7 @@ WebContents::WebContents(v8::Isolate* isolate,
807811

808812
content::WebContents::CreateParams params(session->browser_context());
809813
auto* view = new OffScreenWebContentsView(
810-
transparent,
814+
transparent, offscreen_use_shared_texture_,
811815
base::BindRepeating(&WebContents::OnPaint, base::Unretained(this)));
812816
params.view = view;
813817
params.delegate_view = view;
@@ -3509,8 +3513,10 @@ bool WebContents::IsOffScreen() const {
35093513
return type_ == Type::kOffScreen;
35103514
}
35113515

3512-
void WebContents::OnPaint(const gfx::Rect& dirty_rect, const SkBitmap& bitmap) {
3513-
Emit("paint", dirty_rect, gfx::Image::CreateFrom1xBitmap(bitmap));
3516+
void WebContents::OnPaint(const gfx::Rect& dirty_rect,
3517+
const SkBitmap& bitmap,
3518+
const OffscreenSharedTexture& tex) {
3519+
Emit("paint", dirty_rect, gfx::Image::CreateFrom1xBitmap(bitmap), tex);
35143520
}
35153521

35163522
void WebContents::StartPainting() {

shell/browser/api/electron_api_web_contents.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include "shell/browser/background_throttling_source.h"
4444
#include "shell/browser/event_emitter_mixin.h"
4545
#include "shell/browser/extended_web_contents_observer.h"
46+
#include "shell/browser/osr/osr_paint_event.h"
4647
#include "shell/browser/ui/inspectable_web_contents.h"
4748
#include "shell/browser/ui/inspectable_web_contents_delegate.h"
4849
#include "shell/browser/ui/inspectable_web_contents_view_delegate.h"
@@ -300,7 +301,9 @@ class WebContents : public ExclusiveAccessContext,
300301

301302
// Methods for offscreen rendering
302303
bool IsOffScreen() const;
303-
void OnPaint(const gfx::Rect& dirty_rect, const SkBitmap& bitmap);
304+
void OnPaint(const gfx::Rect& dirty_rect,
305+
const SkBitmap& bitmap,
306+
const OffscreenSharedTexture& info);
304307
void StartPainting();
305308
void StopPainting();
306309
bool IsPainting() const;
@@ -833,6 +836,9 @@ class WebContents : public ExclusiveAccessContext,
833836

834837
bool offscreen_ = false;
835838

839+
// Whether offscreen rendering use gpu shared texture
840+
bool offscreen_use_shared_texture_ = false;
841+
836842
// Whether window is fullscreened by HTML5 api.
837843
bool html_fullscreen_ = false;
838844

shell/browser/osr/osr_host_display_client.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ void LayeredWindowUpdater::Draw(const gfx::Rect& damage_rect,
7070

7171
if (active_ && canvas_->peekPixels(&pixmap)) {
7272
bitmap.installPixels(pixmap);
73-
callback_.Run(damage_rect, bitmap);
73+
callback_.Run(damage_rect, bitmap, {});
7474
}
7575

7676
std::move(draw_callback).Run();

0 commit comments

Comments
 (0)