electron/shell/browser/osr
electron-roller[bot] 3e12da8ed1
chore: bump chromium to 142.0.7444.23 (39-x-y) (#48307)
* chore: bump chromium in DEPS to 142.0.7432.2

* chore: bump chromium in DEPS to 142.0.7434.1

* chore: bump chromium in DEPS to 142.0.7436.1

* chore: bump chromium in DEPS to 142.0.7438.1

* chore: bump chromium in DEPS to 142.0.7440.1

* chore: bump chromium in DEPS to 142.0.7442.1

* chore: bump chromium in DEPS to 142.0.7444.1

* chore: bump chromium in DEPS to 142.0.7444.6

* chore: bump chromium in DEPS to 142.0.7444.3

* 6973697: Use type tags for data stored in V8 internal fields

https://chromium-review.googlesource.com/c/chromium/src/+/6973697

* chore: update patches

* chore: update filenames.libcxx.gni

* fix: parse macOS SDK version across line break
https://chromium-review.googlesource.com/c/chromium/src/+/6980166

(cherry picked from commit 2bcbb33de04fa13e7c923b2420f89c3846f5988b)

* fix: replace v8::Object::SetPrototype() usage
https://chromium-review.googlesource.com/c/v8/v8/+/6983465
https://github.com/nodejs/node/pull/55453

(cherry picked from commit c31b9ed5ac84bbd111c72273d9334af6c50ed374)

* fix: replace additional usages of SetPrototype
https://chromium-review.googlesource.com/c/v8/v8/+/6983465

(cherry picked from commit bf151e9d28520c7dd74cba62240acbcaaab5433d)

* fixup! fix: replace additional usages of SetPrototype https://chromium-review.googlesource.com/c/v8/v8/+/6983465

(cherry picked from commit f4434755b82b098e4d83d42bab26f183b6824f99)

* build: use macos 15 minimum
https://chromium-review.googlesource.com/c/chromium/src/+/6980166

(cherry picked from commit 4d2b5d7b2cf9a1786cdb1a77bf73e4ad0d3e45d1)

* fixup! build: use macos 15 minimum https://chromium-review.googlesource.com/c/chromium/src/+/6980166

(cherry picked from commit 94bb41a66330dcaf6b92c80cfefd72759405793d)

* ci: ignore missing dir for strip_universal_deep

(cherry picked from commit 634963f171bc5f6050151f76973e7ffbab0e00cf)

* chore: update patches

* chore: update patches

* chore: bump chromium in DEPS to 142.0.7444.23

* fix: disable C++ modules in electron_lib builds
https://chromium-review.googlesource.com/c/chromium/src/+/6950738

(cherry picked from commit 6207c79aecae04675b1e258ec14025c3ddfdf270)

* Revert "build: use macos 15 minimum"

This reverts commit 2fc12d6acc1b24f3cbd0adb03122bf6b21eb14b9.

Initially this change was made to test if it fixes libcxx
compilation issues. As that's now resolved by disabling libcxx
modules, this can be reverted.

(cherry picked from commit ad52007d5baffc3da65c0a994943f25da0c3f1c2)

* fix: js2c compilation failure

https://chromium-review.googlesource.com/c/chromium/src/+/6950738

See patch description explaining MacOS 26 SDK headers incompatibility.

(cherry picked from commit 39e2470875cdbf20b86c30ee2c5caa8845465434)

* fix: disable C++ modules in libnode builds

(cherry picked from commit fd0a7b61a151c92729da41eba63fb7ee5b0beebc)

* fixup! fix: replace v8::Object::SetPrototype() usage https://chromium-review.googlesource.com/c/v8/v8/+/6983465 https://github.com/nodejs/node/pull/55453

(cherry picked from commit 2f52159b71ee4aa779dfd3e3050f3b09c2664c36)

* build: switch to macos-15 runner

build/mac/find_sdk.py now requires macOS 15 SDK as a minimum version. The
macos 15 runners default to an Xcode using the 15 SDK and removes older
versions.

(cherry picked from commit e368703f24577e73d904c684a0b4ae53bacfaef2)

* chore: update patches

* fix: partially revert is_headless_mode removal
https://chromium-review.googlesource.com/c/chromium/src/+/6955633

This patch should likely be reworked. For now, this partially reverts the
removal of a required class property to restore behavior.

(cherry picked from commit aff3bf9a244608863bc96b3e2aef911158b29574)

* 6938086: Rename native_widget_types.h -> native_ui_types.h | https://chromium-review.googlesource.com/c/chromium/src/+/6938086

(cherry picked from commit c95ac7bf2b1eda493167b8e36c59d70d86d51429)

* 6973697: Use type tags for data stored in V8 internal fields

https://chromium-review.googlesource.com/c/chromium/src/+/6973697

* fixup! fix: check new forced colors enum value https://chromium-review.googlesource.com/c/chromium/src/+/6944403

(cherry picked from commit 0829c74b2fbcdf03ca462b4b0b76efd727d3d891)

* fix: check new forced colors enum value
https://chromium-review.googlesource.com/c/chromium/src/+/6944403

(cherry picked from commit d5858798074719d19d041fa291c3fd1af8d17f5d)

* feat: add new memory-eviction exit reason
https://chromium-review.googlesource.com/c/chromium/src/+/6991933

(cherry picked from commit 6e63197a2292aece65cd52b7b849d3ff3d10bb90)

* fix: views::NonClientFrameView -> views::FrameView
https://chromium-review.googlesource.com/c/chromium/src/+/7005027
https://chromium-review.googlesource.com/c/chromium/src/+/6966937

(cherry picked from commit 1e86b6ddfb2d19b5bfe30e7539f0a377ffa907ab)

* fix: migrate NetworkConditions -> MatchedNetworkConditions
https://chromium-review.googlesource.com/c/chromium/src/+/6827307

(cherry picked from commit 97100ac1682053d3447e63ed5f03dc2d9938e6ca)

* fix: provide DeviceEmulationCacheBehavior param
https://chromium-review.googlesource.com/c/chromium/src/+/6965238

(cherry picked from commit f9a08c53846ab269c57c14eae6b1c03b163fb30c)

* fix: add missing image_skia include
https://chromium-review.googlesource.com/c/chromium/src/+/6986762

(cherry picked from commit dd5eaf03fd7fbfd49afbe3259c5bf036be566bd9)

* fixup! fix: add missing image_skia include https://chromium-review.googlesource.com/c/chromium/src/+/6986762

(cherry picked from commit 249c4d4de1df4d1588d6fa6fcf5f33b43a6c0f62)

* 6948286: [wasm-imported-strings] Drop feature flag

https://chromium-review.googlesource.com/c/v8/v8/+/6948286

* fix: disable protocol handler DCHECK

https://chromium-review.googlesource.com/c/chromium/src/+/6727594

Ignore the extension custom protocol handler registry DCHECK until
we invest in supporting it. Replacing this DCHECK seems harmless
and will unblock the roll.

(cherry picked from commit 019d3f0b09aeff8aed7991d9669a4ba7f265808b)

* 6986762: Remove some includes of //ui/gfx/image/image_skia.h

https: //chromium-review.googlesource.com/c/chromium/src/+/6986762

* fixup! fix: migrate NetworkConditions -> MatchedNetworkConditions https://chromium-review.googlesource.com/c/chromium/src/+/6827307

(cherry picked from commit a8f67f1ac3f8b07354d9457be9addf242ff70000)

* fixup: 6986762: Remove some includes of //ui/gfx/image/image_skia.h

s

* fix: replace deprecated usage of SetPrototype
https://chromium-review.googlesource.com/c/v8/v8/+/6983465

(cherry picked from commit 5435d87b40c15316bc8828fbc197be647b39b7bb)

* chore: restore electron embedder data tag patch

Co-Authored-By: Sam Maddock <sam@samuelmaddock.com>

* chore: update patches

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
Co-authored-by: Samuel Maddock <smaddock@slack-corp.com>
Co-authored-by: Alice Zhao <alicelovescake@anthropic.com>
Co-authored-by: Sam Maddock <sam@samuelmaddock.com>
2025-10-14 11:25:30 -04:00
..
osr_host_display_client.cc chore: bump chromium to 134.0.6968.0 (main) (#45172) 2025-01-23 23:07:43 -05:00
osr_host_display_client.h chore: bump chromium to 142.0.7444.23 (39-x-y) (#48307) 2025-10-14 11:25:30 -04:00
osr_host_display_client_mac.mm feat: GPU shared texture offscreen rendering (#42953) 2024-08-22 19:23:13 -05:00
osr_paint_event.cc feat: GPU shared texture offscreen rendering (#42953) 2024-08-22 19:23:13 -05:00
osr_paint_event.h chore: bump chromium to 142.0.7444.23 (39-x-y) (#48307) 2025-10-14 11:25:30 -04:00
osr_render_widget_host_view.cc chore: bump chromium to 142.0.7444.23 (39-x-y) (#48307) 2025-10-14 11:25:30 -04:00
osr_render_widget_host_view.h chore: bump chromium to 141.0.7346.0 (main) (#47983) 2025-08-11 12:57:31 +09:00
osr_video_consumer.cc feat: paint event move texture data to handle, add colorSpace (#47315) 2025-08-08 10:13:56 +02:00
osr_video_consumer.h chore: bump chromium to 142.0.7417.0 (39-x-y) (#48363) 2025-09-24 11:56:54 -04:00
osr_view_proxy.cc refactor: inline simple getters (#41125) 2024-01-29 20:43:28 -06:00
osr_view_proxy.h chore: bump chromium to 141.0.7346.0 (main) (#47983) 2025-08-11 12:57:31 +09:00
osr_web_contents_view.cc refactor: remove CreateViewForWidget patch (#46981) 2025-05-14 21:43:23 +09:00
osr_web_contents_view.h chore: bump chromium to 132.0.6807.0 (main) (#44360) 2024-11-04 09:10:00 -05:00
osr_web_contents_view_mac.mm chore: bump chromium to 136.0.7095.0 (main) (#46118) 2025-04-03 19:02:49 -05:00
README.md docs: add docs about shared texture mode osr (#45670) 2025-02-27 11:10:58 -05:00

Offscreen Rendering

Shared Texture Mode

This section provides a brief summary about how an offscreen frame is generated and how to handle it in native code. This only applies to the GPU-accelerated mode with shared texture, when webPreferences.offscreen.useSharedTexture is set to true.

Life of an Offscreen Frame

This is written at the time of Chromium 134 / Electron 35. The code may change in the future. The following description may not completely reflect the procedure, but it generally describes the process.

Initialization

  1. Electron JS creates a BrowserWindow with webPreferences.offscreen set to true.
  2. Electron C++ creates OffScreenRenderWidgetHostView, a subclass of RenderWidgetHostViewBase.
  3. It instantiates an OffScreenVideoConsumer, passing itself as a reference as view_.
  4. The OffScreenVideoConsumer calls view_->CreateVideoCapturer(), which makes Chromium code use HostFrameSinkManager to communicate with FrameSinkManagerImpl (in the Renderer Process) to create a ClientFrameSinkVideoCapturer and a FrameSinkVideoCapturerImpl (in the Renderer Process). It stores ClientFrameSinkVideoCapturer in video_capturer_.
  5. The OffScreenVideoConsumer registers the capture callback to OffScreenRenderWidgetHostView::OnPaint.
  6. It sets the target FPS, size constraints for capture, and calls video_capturer_->Start with the parameter viz::mojom::BufferFormatPreference::kPreferGpuMemoryBuffer to enable shared texture mode and start capturing.
  7. The FrameSinkVideoCapturerImpl accepts kPreferGpuMemoryBuffer and creates a GpuMemoryBufferVideoFramePool to copy the captured frame. The capacity is kFramePoolCapacity, currently 10, meaning it can capture at most 10 frames if the consumer doesn't consume them in time. It is stored in frame_pool_.
  8. The GpuMemoryBufferVideoFramePool creates a RenderableGpuMemoryBufferVideoFramePool using GmbVideoFramePoolContext as the context provider, responsible for creating GpuMemoryBuffer (or MappableSharedImage, as the Chromium team is removing the concept of GpuMemoryBuffer and may replace it with MappableSI in the future).
  9. The GmbVideoFramePoolContext initializes itself in both the Renderer Process and GPU Process.

Capturing

  1. The FrameSinkVideoCapturerImpl starts a capture when it receives an OnFrameDamaged event or is explicitly requested to refresh the frame. All event sources are evaluated by VideoCaptureOracle to see if the capture frequency meets the limit.
  2. If a frame is determined to be captured, FrameSinkVideoCapturerImpl calls frame_pool_->ReserveVideoFrame() to make the pool allocate a frame. The GmbVideoFramePoolContext then communicates with the GPU Process to create an actual platform-dependent texture (e.g., ID3D11Texture2D or IOSurface) that supports being shared across processes.
  3. The GPU Process wraps it into a GpuMemoryBuffer and sends it back to the Renderer Process, and the pool stores it for further usage.
  4. The FrameSinkVideoCapturerImpl then uses this allocated (or reused) frame to create a CopyOutputRequest and calls resolved_target_->RequestCopyOfOutput to copy the frame to the target texture. The resolved_target_ is a CapturableFrameSink that was previously resolved when calling CreateVideoCapturer using OffScreenRenderWidgetHostView.
  5. The GPU Process receives the request and renders the frame to the target texture using the requested format (e.g., RGBA). It then sends a completed event to the Renderer Process FrameSinkVideoCapturerImpl.
  6. The FrameSinkVideoCapturerImpl receives the completed event, provides feedback to the VideoCaptureOracle, and then calls frame_pool_->CloneHandleForDelivery with the captured frame to get a serializable handle to the frame (HANDLE or IOSurfaceRef). On Windows, it calls DuplicateHandle to create a new handle.
  7. It then creates a VideoFrameInfo with the frame info and the handle and calls consumer_->OnFrameCaptured to deliver the frame to the consumer.

Consuming

  1. OffScreenVideoConsumer::OnFrameCaptured is called when the frame is captured. It creates an Electron C++ struct OffscreenSharedTextureValue to extract the required info and handle from the callback. It then creates an OffscreenReleaserHolder to take ownership of the handle and the mojom remote releaser to prevent releasing.
  2. It calls the callback_ with the OffscreenSharedTextureValue, which goes to OffScreenRenderWidgetHostView::OnPaint. When shared texture mode is enabled, it directly redirects the callback to an OnPaintCallback target set during the initialization of OffScreenRenderWidgetHostView, currently set by OffScreenWebContentsView, whose callback_ is also set during initialization. Finally, it goes to WebContents::OnPaint.
  3. The WebContents::OnPaint uses gin_converter (osr_converter.cc) to convert the OffscreenSharedTextureValue to a v8::Object. It converts most of the value to a corresponding v8::Value, and the handle is converted to a Buffer. It also creates a release function to destroy the releaser and free the frame we previously took ownership of. The frame can now be reused for further capturing. Finally, it creates a release monitor to detect if the release function is called before the garbage collector destroys the JS object; if not, it prints a warning.
  4. The data is then emitted to the paint event of webContents. You can now grab the data and pass it to your native code for further processing. You can pass the textureInfo to other processes using Electron IPC, but you can only release it in the main process. Do not keep it for long, or it will drain the buffer pool.

Native Handling

You now have the texture info for the frame. Here's how you should handle it in native code. Suppose you write a node native addon to handle the shared texture.

Retrieve the handle from the textureInfo.sharedTextureHandle. You can also read the buffer in JS and use other methods.

auto textureInfo = args[0];
auto sharedTextureHandle =
    NAPI_GET_PROPERTY_VALUE(textureInfo, "sharedTextureHandle");

size_t handleBufferSize;
uint8_t* handleBufferData;
napi_get_buffer_info(env, sharedTextureHandle,
                      reinterpret_cast<void**>(&handleBufferData),
                      &handleBufferSize);

Import the handle to your rendering program.

// Windows
HANDLE handle = *reinterpret_cast<HANDLE*>(handleBufferData);
Microsoft::WRL::ComPtr<ID3D11Texture2D> shared_texture = nullptr;
HRESULT hr = device1->OpenSharedResource1(handle, IID_PPV_ARGS(&shared_texture)); 

// Extract the texture description
D3D11_TEXTURE2D_DESC desc;
shared_texture->GetDesc(&desc);

// Cache the staging texture if it does not exist or size has changed
if (!cached_staging_texture || cached_width != desc.Width ||
    cached_height != desc.Height) {
  if (cached_staging_texture) {
    cached_staging_texture->Release();
  }

  desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
  desc.Usage = D3D11_USAGE_STAGING;
  desc.BindFlags = 0;
  desc.MiscFlags = 0;

  std::cout << "Create staging Texture2D width=" << desc.Width
            << " height=" << desc.Height << std::endl;
  hr = device->CreateTexture2D(&desc, nullptr, &cached_staging_texture);

  cached_width = desc.Width;
  cached_height = desc.Height;
}

// Copy to a intermediate texture
context->CopyResource(cached_staging_texture.Get(), shared_texture.Get());
// macOS
IOSurfaceRef handle = *reinterpret_cast<IOSurfaceRef*>(handleBufferData);

// Assume you have created a GL context.

GLuint io_surface_tex;
glGenTextures(1, &io_surface_tex);
glEnable(GL_TEXTURE_RECTANGLE_ARB);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, io_surface_tex);

CGLContextObj cgl_context = CGLGetCurrentContext();

GLsizei width = (GLsizei)IOSurfaceGetWidth(io_surface);
GLsizei height = (GLsizei)IOSurfaceGetHeight(io_surface);

CGLTexImageIOSurface2D(cgl_context, GL_TEXTURE_RECTANGLE_ARB, GL_RGBA8, width,
                        height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
                        io_surface, 0);

glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);

// Copy to a intermediate texture from io_surface_tex
// ...

As introduced above, the shared texture is not a single fixed texture that Chromium draws on every frame (CEF before Chromium 103 works that like, please note this significate difference). It is a pool of textures, so every frame Chromium may pass a different texture to you. As soon as you call release, the texture will be reused by Chromium and may cause picture corruption if you keep reading from it. It is also wrong to only open the handle once and directly read from that opened texture on later events. Be very careful if you want to cache the opened texture(s), on Windows, the duplicated handle's value will not be a reliable mapping to the actual underlying texture.

The suggested way is always open the handle on every event, and copy the shared texture to a intermediate texture that you own and release it as soon as possible. You can use the copied texture for further rendering whenever you want. Opening a shared texture should only takes couple microseconds, and you can also use the damaged rect to only copy a portion of the texture to speed up.

You can also refer to these examples: