webgpu: Use compositor_api instead of webrender_api (#37713)

webgpu currently sends updates WebRender directly via WebRender API
(same as webgl that I also plan to reform). 2D canvas uses Compositor
API for that and with this PR so does webgpu. This will be helpful for
#35733, where compositor must know state of image updates.

Testing: WebGPU CTS run:
https://github.com/sagudev/servo/actions/runs/15895299748

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
This commit is contained in:
sagudev 2025-06-26 12:12:29 +02:00 committed by GitHub
parent 6656a09f8c
commit c8132137cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 38 additions and 86 deletions

View file

@ -164,8 +164,6 @@ use style_traits::CSSPixel;
use webgpu::swapchain::WGPUImageMap; use webgpu::swapchain::WGPUImageMap;
#[cfg(feature = "webgpu")] #[cfg(feature = "webgpu")]
use webgpu_traits::{WebGPU, WebGPURequest}; use webgpu_traits::{WebGPU, WebGPURequest};
#[cfg(feature = "webgpu")]
use webrender::RenderApi;
use webrender::RenderApiSender; use webrender::RenderApiSender;
use webrender_api::units::LayoutVector2D; use webrender_api::units::LayoutVector2D;
use webrender_api::{DocumentId, ExternalScrollId, ImageKey}; use webrender_api::{DocumentId, ExternalScrollId, ImageKey};
@ -222,9 +220,6 @@ struct MessagePortInfo {
#[cfg(feature = "webgpu")] #[cfg(feature = "webgpu")]
/// Webrender related objects required by WebGPU threads /// Webrender related objects required by WebGPU threads
struct WebrenderWGPU { struct WebrenderWGPU {
/// Webrender API.
webrender_api: RenderApi,
/// List of Webrender external images /// List of Webrender external images
webrender_external_images: Arc<Mutex<WebrenderExternalImageRegistry>>, webrender_external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
@ -362,10 +357,6 @@ pub struct Constellation<STF, SWF> {
/// memory profiler thread. /// memory profiler thread.
mem_profiler_chan: mem::ProfilerChan, mem_profiler_chan: mem::ProfilerChan,
/// A single WebRender document the constellation operates on.
#[cfg(feature = "webgpu")]
webrender_document: DocumentId,
/// Webrender related objects required by WebGPU threads /// Webrender related objects required by WebGPU threads
#[cfg(feature = "webgpu")] #[cfg(feature = "webgpu")]
webrender_wgpu: WebrenderWGPU, webrender_wgpu: WebrenderWGPU,
@ -657,7 +648,6 @@ where
#[cfg(feature = "webgpu")] #[cfg(feature = "webgpu")]
let webrender_wgpu = WebrenderWGPU { let webrender_wgpu = WebrenderWGPU {
webrender_api: state.webrender_api_sender.create_api(),
webrender_external_images: state.webrender_external_images, webrender_external_images: state.webrender_external_images,
wgpu_image_map: state.wgpu_image_map, wgpu_image_map: state.wgpu_image_map,
}; };
@ -705,8 +695,6 @@ where
webdriver: WebDriverData::new(), webdriver: WebDriverData::new(),
document_states: HashMap::new(), document_states: HashMap::new(),
#[cfg(feature = "webgpu")] #[cfg(feature = "webgpu")]
webrender_document: state.webrender_document,
#[cfg(feature = "webgpu")]
webrender_wgpu, webrender_wgpu,
shutting_down: false, shutting_down: false,
handled_warnings: VecDeque::new(), handled_warnings: VecDeque::new(),
@ -2052,8 +2040,7 @@ where
}; };
let webgpu_chan = match browsing_context_group.webgpus.entry(host) { let webgpu_chan = match browsing_context_group.webgpus.entry(host) {
Entry::Vacant(v) => start_webgpu_thread( Entry::Vacant(v) => start_webgpu_thread(
self.webrender_wgpu.webrender_api.create_sender(), self.compositor_proxy.cross_process_compositor_api.clone(),
self.webrender_document,
self.webrender_wgpu.webrender_external_images.clone(), self.webrender_wgpu.webrender_external_images.clone(),
self.webrender_wgpu.wgpu_image_map.clone(), self.webrender_wgpu.wgpu_image_map.clone(),
) )

View file

@ -6,7 +6,6 @@ use log::warn;
use swapchain::WGPUImageMap; use swapchain::WGPUImageMap;
pub use swapchain::{ContextData, WGPUExternalImages}; pub use swapchain::{ContextData, WGPUExternalImages};
use webgpu_traits::{WebGPU, WebGPUMsg}; use webgpu_traits::{WebGPU, WebGPUMsg};
use webrender::RenderApiSender;
use wgpu_thread::WGPU; use wgpu_thread::WGPU;
pub use {wgpu_core as wgc, wgpu_types as wgt}; pub use {wgpu_core as wgc, wgpu_types as wgt};
@ -16,16 +15,14 @@ mod wgpu_thread;
use std::borrow::Cow; use std::borrow::Cow;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use compositing_traits::WebrenderExternalImageRegistry; use compositing_traits::{CrossProcessCompositorApi, WebrenderExternalImageRegistry};
use ipc_channel::ipc::{self, IpcReceiver}; use ipc_channel::ipc::{self, IpcReceiver};
use servo_config::pref; use servo_config::pref;
use webrender_api::DocumentId;
pub mod swapchain; pub mod swapchain;
pub fn start_webgpu_thread( pub fn start_webgpu_thread(
webrender_api_sender: RenderApiSender, compositor_api: CrossProcessCompositorApi,
webrender_document: DocumentId,
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>, external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
wgpu_image_map: WGPUImageMap, wgpu_image_map: WGPUImageMap,
) -> Option<(WebGPU, IpcReceiver<WebGPUMsg>)> { ) -> Option<(WebGPU, IpcReceiver<WebGPUMsg>)> {
@ -62,8 +59,7 @@ pub fn start_webgpu_thread(
receiver, receiver,
sender_clone, sender_clone,
script_sender, script_sender,
webrender_api_sender, compositor_api,
webrender_document,
external_images, external_images,
wgpu_image_map, wgpu_image_map,
) )

View file

@ -8,7 +8,10 @@ use std::slice;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use arrayvec::ArrayVec; use arrayvec::ArrayVec;
use compositing_traits::{WebrenderExternalImageApi, WebrenderImageSource}; use compositing_traits::{
CrossProcessCompositorApi, ImageUpdate, SerializableImageData, WebrenderExternalImageApi,
WebrenderImageSource,
};
use euclid::default::Size2D; use euclid::default::Size2D;
use ipc_channel::ipc::IpcSender; use ipc_channel::ipc::IpcSender;
use log::{error, warn}; use log::{error, warn};
@ -17,11 +20,10 @@ use serde::{Deserialize, Serialize};
use webgpu_traits::{ use webgpu_traits::{
ContextConfiguration, Error, PRESENTATION_BUFFER_COUNT, WebGPUContextId, WebGPUMsg, ContextConfiguration, Error, PRESENTATION_BUFFER_COUNT, WebGPUContextId, WebGPUMsg,
}; };
use webrender::{RenderApi, Transaction};
use webrender_api::units::DeviceIntSize; use webrender_api::units::DeviceIntSize;
use webrender_api::{ use webrender_api::{
DirtyRect, DocumentId, ExternalImageData, ExternalImageId, ExternalImageType, ImageData, ExternalImageData, ExternalImageId, ExternalImageType, ImageDescriptor, ImageDescriptorFlags,
ImageDescriptor, ImageDescriptorFlags, ImageFormat, ImageKey, ImageFormat, ImageKey,
}; };
use wgpu_core::device::HostMap; use wgpu_core::device::HostMap;
use wgpu_core::global::Global; use wgpu_core::global::Global;
@ -182,7 +184,7 @@ impl WebGPUImageDescriptor {
pub struct ContextData { pub struct ContextData {
image_key: ImageKey, image_key: ImageKey,
image_desc: WebGPUImageDescriptor, image_desc: WebGPUImageDescriptor,
image_data: ImageData, image_data: ExternalImageData,
buffer_ids: ArrayVec<(id::BufferId, PresentationBufferState), PRESENTATION_BUFFER_COUNT>, buffer_ids: ArrayVec<(id::BufferId, PresentationBufferState), PRESENTATION_BUFFER_COUNT>,
/// If there is no associated swapchain the context is dummy (transparent black) /// If there is no associated swapchain the context is dummy (transparent black)
swap_chain: Option<SwapChain>, swap_chain: Option<SwapChain>,
@ -202,12 +204,12 @@ impl ContextData {
size: DeviceIntSize, size: DeviceIntSize,
buffer_ids: ArrayVec<id::BufferId, PRESENTATION_BUFFER_COUNT>, buffer_ids: ArrayVec<id::BufferId, PRESENTATION_BUFFER_COUNT>,
) -> Self { ) -> Self {
let image_data = ImageData::External(ExternalImageData { let image_data = ExternalImageData {
id: ExternalImageId(context_id.0), id: ExternalImageId(context_id.0),
channel_index: 0, channel_index: 0,
image_type: ExternalImageType::Buffer, image_type: ExternalImageType::Buffer,
normalized_uvs: false, normalized_uvs: false,
}); };
Self { Self {
image_key, image_key,
@ -300,8 +302,7 @@ impl ContextData {
mut self, mut self,
global: &Arc<Global>, global: &Arc<Global>,
script_sender: &IpcSender<WebGPUMsg>, script_sender: &IpcSender<WebGPUMsg>,
webrender_api: &Arc<Mutex<RenderApi>>, compositor_api: &CrossProcessCompositorApi,
webrender_document: DocumentId,
) { ) {
self.destroy_swapchain(global); self.destroy_swapchain(global);
for (buffer_id, _) in self.buffer_ids { for (buffer_id, _) in self.buffer_ids {
@ -309,12 +310,7 @@ impl ContextData {
warn!("Unable to send FreeBuffer({:?}) ({:?})", buffer_id, e); warn!("Unable to send FreeBuffer({:?}) ({:?})", buffer_id, e);
}; };
} }
let mut txn = Transaction::new(); compositor_api.update_images(vec![ImageUpdate::DeleteImage(self.image_key)]);
txn.delete_image(self.image_key);
webrender_api
.lock()
.unwrap()
.send_transaction(webrender_document, txn);
} }
/// Returns true if presentation id was updated (was newer) /// Returns true if presentation id was updated (was newer)
@ -344,17 +340,11 @@ impl crate::WGPU {
buffer_ids: ArrayVec<id::BufferId, PRESENTATION_BUFFER_COUNT>, buffer_ids: ArrayVec<id::BufferId, PRESENTATION_BUFFER_COUNT>,
) { ) {
let context_data = ContextData::new(context_id, image_key, size, buffer_ids); let context_data = ContextData::new(context_id, image_key, size, buffer_ids);
let mut txn = Transaction::new(); self.compositor_api.add_image(
txn.add_image(
image_key, image_key,
context_data.image_desc.0, context_data.image_desc.0,
context_data.image_data.clone(), SerializableImageData::External(context_data.image_data),
None,
); );
self.webrender_api
.lock()
.unwrap()
.send_transaction(self.webrender_document, txn);
assert!( assert!(
self.wgpu_image_map self.wgpu_image_map
.lock() .lock()
@ -431,17 +421,12 @@ impl crate::WGPU {
}; };
if needs_image_update { if needs_image_update {
let mut txn = Transaction::new(); self.compositor_api
txn.update_image( .update_images(vec![ImageUpdate::UpdateImage(
context_data.image_key, context_data.image_key,
context_data.image_desc.0, context_data.image_desc.0,
context_data.image_data.clone(), SerializableImageData::External(context_data.image_data),
&DirtyRect::All, )]);
);
self.webrender_api
.lock()
.unwrap()
.send_transaction(self.webrender_document, txn);
} }
} }
@ -521,8 +506,7 @@ impl crate::WGPU {
let callback = { let callback = {
let global = Arc::clone(&self.global); let global = Arc::clone(&self.global);
let wgpu_image_map = Arc::clone(&self.wgpu_image_map); let wgpu_image_map = Arc::clone(&self.wgpu_image_map);
let webrender_api = Arc::clone(&self.webrender_api); let compositor_api = self.compositor_api.clone();
let webrender_document = self.webrender_document;
let token = self.poller.token(); let token = self.poller.token();
Box::new(move |result| { Box::new(move |result| {
drop(token); drop(token);
@ -532,8 +516,7 @@ impl crate::WGPU {
buffer_id, buffer_id,
wgpu_image_map, wgpu_image_map,
context_id, context_id,
webrender_api, compositor_api,
webrender_document,
image_desc, image_desc,
presentation_id, presentation_id,
); );
@ -554,12 +537,7 @@ impl crate::WGPU {
.unwrap() .unwrap()
.remove(&context_id) .remove(&context_id)
.unwrap() .unwrap()
.destroy( .destroy(&self.global, &self.script_sender, &self.compositor_api);
&self.global,
&self.script_sender,
&self.webrender_api,
self.webrender_document,
);
} }
} }
@ -570,8 +548,7 @@ fn update_wr_image(
buffer_id: id::BufferId, buffer_id: id::BufferId,
wgpu_image_map: WGPUImageMap, wgpu_image_map: WGPUImageMap,
context_id: WebGPUContextId, context_id: WebGPUContextId,
webrender_api: Arc<Mutex<RenderApi>>, compositor_api: CrossProcessCompositorApi,
webrender_document: webrender_api::DocumentId,
image_desc: WebGPUImageDescriptor, image_desc: WebGPUImageDescriptor,
presentation_id: PresentationId, presentation_id: PresentationId,
) { ) {
@ -597,17 +574,11 @@ fn update_wr_image(
return; return;
}; };
let old_presentation_buffer = swap_chain.data.replace(presentation_buffer); let old_presentation_buffer = swap_chain.data.replace(presentation_buffer);
let mut txn = Transaction::new(); compositor_api.update_images(vec![ImageUpdate::UpdateImage(
txn.update_image(
context_data.image_key, context_data.image_key,
context_data.image_desc.0, context_data.image_desc.0,
context_data.image_data.clone(), SerializableImageData::External(context_data.image_data),
&DirtyRect::All, )]);
);
webrender_api
.lock()
.unwrap()
.send_transaction(webrender_document, txn);
if let Some(old_presentation_buffer) = old_presentation_buffer { if let Some(old_presentation_buffer) = old_presentation_buffer {
context_data.unmap_old_buffer(old_presentation_buffer) context_data.unmap_old_buffer(old_presentation_buffer)
} }

View file

@ -10,7 +10,9 @@ use std::slice;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use base::id::PipelineId; use base::id::PipelineId;
use compositing_traits::{WebrenderExternalImageRegistry, WebrenderImageHandlerType}; use compositing_traits::{
CrossProcessCompositorApi, WebrenderExternalImageRegistry, WebrenderImageHandlerType,
};
use ipc_channel::ipc::{IpcReceiver, IpcSender, IpcSharedMemory}; use ipc_channel::ipc::{IpcReceiver, IpcSender, IpcSharedMemory};
use log::{info, warn}; use log::{info, warn};
use servo_config::pref; use servo_config::pref;
@ -19,8 +21,7 @@ use webgpu_traits::{
RenderPassId, ShaderCompilationInfo, WebGPU, WebGPUAdapter, WebGPUContextId, WebGPUDevice, RenderPassId, ShaderCompilationInfo, WebGPU, WebGPUAdapter, WebGPUContextId, WebGPUDevice,
WebGPUMsg, WebGPUQueue, WebGPURequest, apply_render_command, WebGPUMsg, WebGPUQueue, WebGPURequest, apply_render_command,
}; };
use webrender::{RenderApi, RenderApiSender}; use webrender_api::ExternalImageId;
use webrender_api::{DocumentId, ExternalImageId};
use wgc::command::{ComputePass, ComputePassDescriptor, RenderPass}; use wgc::command::{ComputePass, ComputePassDescriptor, RenderPass};
use wgc::device::{DeviceDescriptor, ImplicitPipelineIds}; use wgc::device::{DeviceDescriptor, ImplicitPipelineIds};
use wgc::id; use wgc::id;
@ -103,8 +104,7 @@ pub(crate) struct WGPU {
/// because wgpu does not invalidate command encoder object /// because wgpu does not invalidate command encoder object
/// (this is also reused for invalidation of command buffers) /// (this is also reused for invalidation of command buffers)
error_command_encoders: HashMap<id::CommandEncoderId, String>, error_command_encoders: HashMap<id::CommandEncoderId, String>,
pub(crate) webrender_api: Arc<Mutex<RenderApi>>, pub(crate) compositor_api: CrossProcessCompositorApi,
pub(crate) webrender_document: DocumentId,
pub(crate) external_images: Arc<Mutex<WebrenderExternalImageRegistry>>, pub(crate) external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
pub(crate) wgpu_image_map: WGPUImageMap, pub(crate) wgpu_image_map: WGPUImageMap,
/// Provides access to poller thread /// Provides access to poller thread
@ -120,8 +120,7 @@ impl WGPU {
receiver: IpcReceiver<WebGPURequest>, receiver: IpcReceiver<WebGPURequest>,
sender: IpcSender<WebGPURequest>, sender: IpcSender<WebGPURequest>,
script_sender: IpcSender<WebGPUMsg>, script_sender: IpcSender<WebGPUMsg>,
webrender_api_sender: RenderApiSender, compositor_api: CrossProcessCompositorApi,
webrender_document: DocumentId,
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>, external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
wgpu_image_map: WGPUImageMap, wgpu_image_map: WGPUImageMap,
) -> Self { ) -> Self {
@ -150,8 +149,7 @@ impl WGPU {
global, global,
devices: Arc::new(Mutex::new(HashMap::new())), devices: Arc::new(Mutex::new(HashMap::new())),
error_command_encoders: HashMap::new(), error_command_encoders: HashMap::new(),
webrender_api: Arc::new(Mutex::new(webrender_api_sender.create_api())), compositor_api,
webrender_document,
external_images, external_images,
wgpu_image_map, wgpu_image_map,
compute_passes: HashMap::new(), compute_passes: HashMap::new(),
@ -504,7 +502,7 @@ impl WGPU {
.lock() .lock()
.expect("Lock poisoned?") .expect("Lock poisoned?")
.next_id(WebrenderImageHandlerType::WebGPU); .next_id(WebrenderImageHandlerType::WebGPU);
let image_key = self.webrender_api.lock().unwrap().generate_image_key(); let image_key = self.compositor_api.generate_image_key().unwrap();
let context_id = WebGPUContextId(id.0); let context_id = WebGPUContextId(id.0);
if let Err(e) = sender.send((context_id, image_key)) { if let Err(e) = sender.send((context_id, image_key)) {
warn!("Failed to send ExternalImageId to new context ({})", e); warn!("Failed to send ExternalImageId to new context ({})", e);