diff --git a/Cargo.lock b/Cargo.lock index 547177675bf..94de394a66d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6876,6 +6876,7 @@ dependencies = [ "cc", "cfg-if", "dirs", + "dpi", "egui", "egui-file-dialog", "egui-winit", @@ -8592,6 +8593,7 @@ name = "webrender_traits" version = "0.0.1" dependencies = [ "base", + "dpi", "embedder_traits", "euclid", "gleam", @@ -8600,6 +8602,7 @@ dependencies = [ "ipc-channel", "libc", "log", + "raw-window-handle", "serde", "servo-media", "servo_geometry", diff --git a/Cargo.toml b/Cargo.toml index ec5d6665565..5e8547f2b59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ ctr = "0.9.2" darling = { version = "0.20", default-features = false } data-url = "0.3" devtools_traits = { path = "components/shared/devtools" } +dpi = { version = "0.1" } embedder_traits = { path = "components/shared/embedder" } encoding_rs = "0.8" env_logger = "0.10" diff --git a/components/compositing/Cargo.toml b/components/compositing/Cargo.toml index 6c7d2df2c1f..4382036accc 100644 --- a/components/compositing/Cargo.toml +++ b/components/compositing/Cargo.toml @@ -36,7 +36,6 @@ script_traits = { workspace = true } servo_config = { path = "../config" } servo_geometry = { path = "../geometry" } style_traits = { workspace = true } -surfman = { workspace = true } tracing = { workspace = true, optional = true } webrender = { workspace = true } webrender_api = { workspace = true } diff --git a/components/servo/examples/winit_minimal.rs b/components/servo/examples/winit_minimal.rs index 0ccacbdeef3..2b15acc8e02 100644 --- a/components/servo/examples/winit_minimal.rs +++ b/components/servo/examples/winit_minimal.rs @@ -8,14 +8,12 @@ use std::rc::Rc; use compositing::windowing::{AnimationState, EmbedderMethods, WindowMethods}; use euclid::{Point2D, Scale, Size2D}; -use servo::{Servo, TouchEventType, WebView}; +use servo::{RenderingContext, Servo, TouchEventAction, WebView, WindowRenderingContext}; use servo_geometry::DeviceIndependentPixel; -use surfman::{Connection, SurfaceType}; use tracing::warn; use url::Url; use webrender_api::units::{DeviceIntPoint, DeviceIntRect, DevicePixel, LayoutVector2D}; use webrender_api::ScrollLocation; -use webrender_traits::SurfmanRenderingContext; use winit::application::ApplicationHandler; use winit::dpi::{PhysicalPosition, PhysicalSize}; use winit::event::{MouseScrollDelta, WindowEvent}; @@ -62,7 +60,7 @@ impl ::servo::WebViewDelegate for AppState { } fn notify_new_frame_ready(&self, _: WebView) { - self.servo.present(); + self.window_delegate.window.request_redraw(); } fn request_open_auxiliary_webview(&self, parent_webview: WebView) -> Option { @@ -87,38 +85,21 @@ impl App { impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { if let Self::Initial(waker) = self { - let window = event_loop - .create_window(Window::default_attributes()) - .expect("Failed to create winit Window"); let display_handle = event_loop .display_handle() .expect("Failed to get display handle"); - let connection = Connection::from_display_handle(display_handle) - .expect("Failed to create connection"); - let adapter = connection - .create_adapter() - .expect("Failed to create adapter"); - let rendering_context = SurfmanRenderingContext::create(&connection, &adapter, None) - .expect("Failed to create rendering context"); - let native_widget = rendering_context - .connection() - .create_native_widget_from_window_handle( - window.window_handle().expect("Failed to get window handle"), - winit_size_to_euclid_size(window.inner_size()) - .to_i32() - .to_untyped(), - ) - .expect("Failed to create native widget"); - let surface = rendering_context - .create_surface(SurfaceType::Widget { native_widget }) - .expect("Failed to create surface"); - rendering_context - .bind_surface(surface) - .expect("Failed to bind surface"); - rendering_context - .make_gl_context_current() - .expect("Failed to make context current"); + let window = event_loop + .create_window(Window::default_attributes()) + .expect("Failed to create winit Window"); + let window_handle = window.window_handle().expect("Failed to get window handle"); + + let rendering_context = + WindowRenderingContext::new(display_handle, window_handle, &window.inner_size()) + .expect("Could not create RenderingContext for window."); let window_delegate = Rc::new(WindowDelegate::new(window)); + + let _ = rendering_context.make_current(); + let servo = Servo::new( Default::default(), Default::default(), @@ -139,7 +120,8 @@ impl ApplicationHandler for App { }); // Make a new WebView and assign the `AppState` as the delegate. - let url = Url::parse("https://servo.org").expect("Guaranteed by argument"); + let url = Url::parse("https://demo.servo.org/experiments/twgl-tunnel/") + .expect("Guaranteed by argument"); let webview = app_state.servo.new_webview(url); webview.set_delegate(app_state.clone()); app_state.webviews.borrow_mut().push(webview); @@ -170,7 +152,7 @@ impl ApplicationHandler for App { }, WindowEvent::RedrawRequested => { if let Self::Running(state) = self { - state.webviews.borrow().last().unwrap().composite(); + state.webviews.borrow().last().unwrap().paint_immediately(); state.servo.present(); } }, @@ -188,7 +170,7 @@ impl ApplicationHandler for App { webview.notify_scroll_event( ScrollLocation::Delta(moved_by), DeviceIntPoint::new(10, 10), - TouchEventType::Down, + TouchEventAction::Down, ); } } diff --git a/components/servo/lib.rs b/components/servo/lib.rs index b2e5028c52e..30164692c9d 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -101,7 +101,11 @@ pub use webgpu; use webgpu::swapchain::WGPUImageMap; use webrender::{RenderApiSender, ShaderPrecacheFlags, UploadMethod, ONE_TIME_USAGE_HINT}; use webrender_api::{ColorF, DocumentId, FramePublishId}; -use webrender_traits::rendering_context::{GLVersion, RenderingContext}; +use webrender_traits::rendering_context::GLVersion; +pub use webrender_traits::rendering_context::{ + OffscreenRenderingContext, RenderingContext, SoftwareRenderingContext, SurfmanRenderingContext, + WindowRenderingContext, +}; use webrender_traits::{ CrossProcessCompositorApi, WebrenderExternalImageHandlers, WebrenderExternalImageRegistry, WebrenderImageHandlerType, @@ -113,7 +117,7 @@ pub use { background_hang_monitor, base, canvas, canvas_traits, compositing, devtools, devtools_traits, euclid, fonts, ipc_channel, layout_thread_2020, media, net, net_traits, profile, profile_traits, script, script_layout_interface, script_traits, servo_config as config, - servo_config, servo_geometry, servo_url, style, style_traits, webrender_api, webrender_traits, + servo_config, servo_geometry, servo_url, style, style_traits, webrender_api, }; #[cfg(feature = "bluetooth")] pub use {bluetooth, bluetooth_traits}; diff --git a/components/servo/webview.rs b/components/servo/webview.rs index e659e5d3073..f7264607d37 100644 --- a/components/servo/webview.rs +++ b/components/servo/webview.rs @@ -414,4 +414,8 @@ impl WebView { .constellation_proxy .send(ConstellationMsg::SendError(Some(self.id()), message)); } + + pub fn paint_immediately(&self) { + self.inner().compositor.borrow_mut().composite(); + } } diff --git a/components/shared/webrender/Cargo.toml b/components/shared/webrender/Cargo.toml index 97eca163107..7211f1815f9 100644 --- a/components/shared/webrender/Cargo.toml +++ b/components/shared/webrender/Cargo.toml @@ -31,3 +31,5 @@ servo_geometry = { path = "../../geometry" } servo-media = { workspace = true } style = { workspace = true } surfman = { workspace = true, features = ["sm-x11"] } +raw-window-handle = { version = "0.6" } +dpi = { version = "0.1" } diff --git a/components/shared/webrender/lib.rs b/components/shared/webrender/lib.rs index 320f5389d9d..e01e635eeb4 100644 --- a/components/shared/webrender/lib.rs +++ b/components/shared/webrender/lib.rs @@ -28,8 +28,6 @@ use webrender_api::{ ImageKey, NativeFontHandle, PipelineId as WebRenderPipelineId, }; -pub use crate::rendering_context::SurfmanRenderingContext; - #[derive(Deserialize, Serialize)] pub enum CrossProcessCompositorMessage { /// Inform WebRender of the existence of this pipeline. diff --git a/components/shared/webrender/rendering_context.rs b/components/shared/webrender/rendering_context.rs index 9e1ef5922f3..631b0b28acf 100644 --- a/components/shared/webrender/rendering_context.rs +++ b/components/shared/webrender/rendering_context.rs @@ -9,12 +9,14 @@ use std::ffi::c_void; use std::num::NonZeroU32; use std::rc::Rc; +use dpi::PhysicalSize; use euclid::default::{Rect, Size2D}; use euclid::Point2D; use gleam::gl::{self, Gl}; use glow::NativeFramebuffer; use image::RgbaImage; use log::{debug, trace, warn}; +use raw_window_handle::{DisplayHandle, WindowHandle}; use servo_media::player::context::{GlContext, NativeDisplay}; use surfman::chains::{PreserveBuffer, SwapChain}; #[cfg(all(target_os = "linux", not(target_env = "ohos")))] @@ -24,8 +26,7 @@ use surfman::platform::generic::multi::context::NativeContext as LinuxNativeCont pub use surfman::Error; use surfman::{ Adapter, Connection, Context, ContextAttributeFlags, ContextAttributes, Device, GLApi, - NativeContext, NativeDevice, NativeWidget, Surface, SurfaceAccess, SurfaceInfo, SurfaceTexture, - SurfaceType, + NativeContext, NativeWidget, Surface, SurfaceAccess, SurfaceInfo, SurfaceTexture, SurfaceType, }; /// Describes the OpenGL version that is requested when a context is created. @@ -81,7 +82,6 @@ pub trait RenderingContext { fn destroy_texture(&self, _surface_texture: SurfaceTexture) -> Option { None } - /// The connection to the display server for WebGL. Default to `None`. fn connection(&self) -> Option { None @@ -96,28 +96,124 @@ pub trait RenderingContext { /// The `SurfmanRenderingContext` struct encapsulates the necessary data and methods /// to interact with the Surfman library, including creating surfaces, binding surfaces, /// resizing surfaces, presenting rendered frames, and managing the OpenGL context state. -#[derive(Clone)] -pub struct SurfmanRenderingContext(Rc); - -struct RenderingContextData { +pub struct SurfmanRenderingContext { gl: Rc, device: RefCell, context: RefCell, - // We either render to a swap buffer or to a native widget - swap_chain: Option>, } -impl Drop for RenderingContextData { +impl Drop for SurfmanRenderingContext { fn drop(&mut self) { let device = &mut self.device.borrow_mut(); let context = &mut self.context.borrow_mut(); - if let Some(ref swap_chain) = self.swap_chain { - let _ = swap_chain.destroy(device, context); - } let _ = device.destroy_context(context); } } +impl SurfmanRenderingContext { + fn new(connection: &Connection, adapter: &Adapter) -> Result { + let mut device = connection.create_device(adapter)?; + + let flags = ContextAttributeFlags::ALPHA | + ContextAttributeFlags::DEPTH | + ContextAttributeFlags::STENCIL; + let gl_api = connection.gl_api(); + let version = match &gl_api { + GLApi::GLES => surfman::GLVersion { major: 3, minor: 0 }, + GLApi::GL => surfman::GLVersion { major: 3, minor: 2 }, + }; + let context_descriptor = + device.create_context_descriptor(&ContextAttributes { flags, version })?; + let context = device.create_context(&context_descriptor, None)?; + + #[allow(unsafe_code)] + let gl = { + match gl_api { + GLApi::GL => unsafe { + gl::GlFns::load_with(|func_name| device.get_proc_address(&context, func_name)) + }, + GLApi::GLES => unsafe { + gl::GlesFns::load_with(|func_name| device.get_proc_address(&context, func_name)) + }, + } + }; + + Ok(SurfmanRenderingContext { + gl, + device: RefCell::new(device), + context: RefCell::new(context), + }) + } + + fn create_surface(&self, surface_type: SurfaceType) -> Result { + let device = &mut self.device.borrow_mut(); + let context = &self.context.borrow(); + device.create_surface(context, SurfaceAccess::GPUOnly, surface_type) + } + + fn bind_surface(&self, surface: Surface) -> Result<(), Error> { + let device = &self.device.borrow(); + let context = &mut self.context.borrow_mut(); + device + .bind_surface_to_context(context, surface) + .map_err(|(err, mut surface)| { + let _ = device.destroy_surface(context, &mut surface); + err + })?; + Ok(()) + } + + fn create_attached_swap_chain(&self) -> Result, Error> { + let device = &mut self.device.borrow_mut(); + let context = &mut self.context.borrow_mut(); + SwapChain::create_attached(device, context, SurfaceAccess::GPUOnly) + } + + fn resize_surface(&self, size: Size2D) -> Result<(), Error> { + let device = &mut self.device.borrow_mut(); + let context = &mut self.context.borrow_mut(); + + let mut surface = device.unbind_surface_from_context(context)?.unwrap(); + device.resize_surface(context, &mut surface, size)?; + device + .bind_surface_to_context(context, surface) + .map_err(|(err, mut surface)| { + let _ = device.destroy_surface(context, &mut surface); + err + }) + } + + fn present_bound_surface(&self) -> Result<(), Error> { + let device = &self.device.borrow(); + let context = &mut self.context.borrow_mut(); + + let mut surface = device.unbind_surface_from_context(context)?.unwrap(); + device.present_surface(context, &mut surface)?; + device + .bind_surface_to_context(context, surface) + .map_err(|(err, mut surface)| { + let _ = device.destroy_surface(context, &mut surface); + err + }) + } + + #[allow(dead_code)] + fn native_context(&self) -> NativeContext { + let device = &self.device.borrow(); + let context = &self.context.borrow(); + device.native_context(context) + } + + fn framebuffer(&self) -> Option { + let device = &self.device.borrow(); + let context = &self.context.borrow(); + device + .context_surface_info(context) + .unwrap_or(None) + .and_then(|info| info.framebuffer_object) + } +} + impl RenderingContext for SurfmanRenderingContext { fn gl_context(&self) -> GlContext { #[cfg(all(target_os = "linux", not(target_env = "ohos")))] @@ -149,10 +245,11 @@ impl RenderingContext for SurfmanRenderingContext { GlContext::Unknown } } + fn gl_display(&self) -> NativeDisplay { #[cfg(all(target_os = "linux", not(target_env = "ohos")))] { - match self.connection().native_connection() { + match self.device.borrow().connection().native_connection() { surfman::NativeConnection::Default(LinuxNativeConnection::Default(connection)) => { NativeDisplay::Egl(connection.0 as usize) }, @@ -166,7 +263,8 @@ impl RenderingContext for SurfmanRenderingContext { { #[cfg(feature = "no-wgl")] { - NativeDisplay::Egl(self.native_device().egl_display as usize) + let device = &self.device.borrow(); + NativeDisplay::Egl(device.native_device().egl_display as usize) } #[cfg(not(feature = "no-wgl"))] NativeDisplay::Unknown @@ -180,55 +278,59 @@ impl RenderingContext for SurfmanRenderingContext { } } - fn prepare_for_rendering(&self) { - self.0.gl.bind_framebuffer( - gleam::gl::FRAMEBUFFER, - self.framebuffer() - .map_or(0, |framebuffer| framebuffer.0.into()), - ); - } - - fn read_to_image(&self, source_rectangle: Rect) -> Option { - let framebuffer_id = self - .framebuffer() - .map(|framebuffer| framebuffer.0.into()) - .unwrap_or(0); - Framebuffer::read_framebuffer_to_image(self.gl_api(), framebuffer_id, source_rectangle) - } - - fn resize(&self, size: Size2D) { - if let Err(err) = self.resize(size) { - warn!("Failed to resize surface: {:?}", err); - } - } - fn present(&self) { - if let Err(err) = self.present() { - warn!("Failed to present surface: {:?}", err); - } - } - fn make_current(&self) -> Result<(), Error> { - self.make_gl_context_current() - } - #[allow(unsafe_code)] - fn gl_api(&self) -> Rc { - self.0.gl.clone() - } fn gl_version(&self) -> GLVersion { - let device = self.0.device.borrow(); - let context = self.0.context.borrow(); + let device = self.device.borrow(); + let context = self.context.borrow(); let descriptor = device.context_descriptor(&context); let attributes = device.context_descriptor_attributes(&descriptor); let major = attributes.version.major; let minor = attributes.version.minor; - match self.connection().gl_api() { + match device.connection().gl_api() { GLApi::GL => GLVersion::GL(major, minor), GLApi::GLES => GLVersion::GLES(major, minor), } } + fn gl_api(&self) -> Rc { + self.gl.clone() + } + + fn prepare_for_rendering(&self) { + let framebuffer_id = self + .framebuffer() + .map_or(0, |framebuffer| framebuffer.0.into()); + self.gl + .bind_framebuffer(gleam::gl::FRAMEBUFFER, framebuffer_id); + } + + fn read_to_image(&self, source_rectangle: Rect) -> Option { + let framebuffer_id = self + .framebuffer() + .map_or(0, |framebuffer| framebuffer.0.into()); + Framebuffer::read_framebuffer_to_image(&self.gl, framebuffer_id, source_rectangle) + } + + fn resize(&self, size: Size2D) { + if let Err(error) = self.resize_surface(size) { + warn!("Error resizing surface: {error:?}"); + } + } + + fn present(&self) { + if let Err(error) = self.present_bound_surface() { + warn!("Error presenting surface: {error:?}"); + } + } + + fn make_current(&self) -> Result<(), Error> { + let device = &self.device.borrow(); + let context = &mut self.context.borrow(); + device.make_context_current(context) + } + fn create_texture(&self, surface: Surface) -> Option<(SurfaceTexture, u32, Size2D)> { - let device = &self.0.device.borrow(); - let context = &mut self.0.context.borrow_mut(); + let device = &self.device.borrow(); + let context = &mut self.context.borrow_mut(); let SurfaceInfo { id: front_buffer_id, size, @@ -244,236 +346,164 @@ impl RenderingContext for SurfmanRenderingContext { } fn destroy_texture(&self, surface_texture: SurfaceTexture) -> Option { - self.destroy_surface_texture(surface_texture).ok() - } - - fn connection(&self) -> Option { - Some(self.connection()) - } -} - -impl SurfmanRenderingContext { - pub fn create( - connection: &Connection, - adapter: &Adapter, - headless: Option>, - ) -> Result { - let mut device = connection.create_device(adapter)?; - let flags = ContextAttributeFlags::ALPHA | - ContextAttributeFlags::DEPTH | - ContextAttributeFlags::STENCIL; - let version = match connection.gl_api() { - GLApi::GLES => surfman::GLVersion { major: 3, minor: 0 }, - GLApi::GL => surfman::GLVersion { major: 3, minor: 2 }, - }; - let context_attributes = ContextAttributes { flags, version }; - let context_descriptor = device.create_context_descriptor(&context_attributes)?; - let mut context = device.create_context(&context_descriptor, None)?; - let surface_access = SurfaceAccess::GPUOnly; - let swap_chain = if let Some(size) = headless { - let surface_type = SurfaceType::Generic { size }; - let surface = device.create_surface(&context, surface_access, surface_type)?; - device - .bind_surface_to_context(&mut context, surface) - .map_err(|(err, mut surface)| { - let _ = device.destroy_surface(&mut context, &mut surface); - err - })?; - device.make_context_current(&context)?; - Some(SwapChain::create_attached( - &mut device, - &mut context, - surface_access, - )?) - } else { - None - }; - - #[allow(unsafe_code)] - let gl = { - match connection.gl_api() { - GLApi::GL => unsafe { - gl::GlFns::load_with(|s| device.get_proc_address(&context, s)) - }, - GLApi::GLES => unsafe { - gl::GlesFns::load_with(|s| device.get_proc_address(&context, s)) - }, - } - }; - - let device = RefCell::new(device); - let context = RefCell::new(context); - let data = RenderingContextData { - gl, - device, - context, - swap_chain, - }; - Ok(SurfmanRenderingContext(Rc::new(data))) - } - - pub fn create_surface( - &self, - surface_type: SurfaceType, - ) -> Result { - let device = &mut self.0.device.borrow_mut(); - let context = &self.0.context.borrow(); - let surface_access = SurfaceAccess::GPUOnly; - device.create_surface(context, surface_access, surface_type) - } - - pub fn bind_surface(&self, surface: Surface) -> Result<(), Error> { - let device = &self.0.device.borrow(); - let context = &mut self.0.context.borrow_mut(); - device - .bind_surface_to_context(context, surface) - .map_err(|(err, mut surface)| { - let _ = device.destroy_surface(context, &mut surface); - err - })?; - - device.make_context_current(context)?; - Ok(()) - } - - pub fn destroy_surface(&self, mut surface: Surface) -> Result<(), Error> { - let device = &self.0.device.borrow(); - let context = &mut self.0.context.borrow_mut(); - device.destroy_surface(context, &mut surface) - } - - pub fn create_surface_texture(&self, surface: Surface) -> Result { - let device = &self.0.device.borrow(); - let context = &mut self.0.context.borrow_mut(); - device - .create_surface_texture(context, surface) - .map_err(|(error, _)| error) - } - - pub fn destroy_surface_texture( - &self, - surface_texture: SurfaceTexture, - ) -> Result { - let device = &self.0.device.borrow(); - let context = &mut self.0.context.borrow_mut(); + let device = &self.device.borrow(); + let context = &mut self.context.borrow_mut(); device .destroy_surface_texture(context, surface_texture) .map_err(|(error, _)| error) + .ok() } - pub fn make_gl_context_current(&self) -> Result<(), Error> { - let device = &self.0.device.borrow(); - let context = &self.0.context.borrow(); - device.make_context_current(context) + fn connection(&self) -> Option { + Some(self.device.borrow().connection()) + } +} + +/// A software rendering context that uses a software OpenGL implementation to render +/// Servo. This will generally have bad performance, but can be used in situations where +/// it is more convenient to have consistent, but slower display output. +/// +/// The results of the render can be accessed via [`RenderingContext::read_to_image`]. +pub struct SoftwareRenderingContext { + surfman_rendering_info: SurfmanRenderingContext, + swap_chain: SwapChain, +} + +impl SoftwareRenderingContext { + pub fn new(size: PhysicalSize) -> Result { + let connection = Connection::new()?; + let adapter = connection.create_software_adapter()?; + let surfman_rendering_info = SurfmanRenderingContext::new(&connection, &adapter)?; + + let size = Size2D::new(size.width as i32, size.height as i32); + let surface = surfman_rendering_info.create_surface(SurfaceType::Generic { size })?; + surfman_rendering_info.bind_surface(surface)?; + surfman_rendering_info.make_current()?; + + let swap_chain = surfman_rendering_info.create_attached_swap_chain()?; + Ok(SoftwareRenderingContext { + surfman_rendering_info, + swap_chain, + }) + } +} + +impl Drop for SoftwareRenderingContext { + fn drop(&mut self) { + let device = &mut self.surfman_rendering_info.device.borrow_mut(); + let context = &mut self.surfman_rendering_info.context.borrow_mut(); + let _ = self.swap_chain.destroy(device, context); + } +} + +impl RenderingContext for SoftwareRenderingContext { + fn gl_context(&self) -> GlContext { + self.surfman_rendering_info.gl_context() } - pub fn swap_chain(&self) -> Result<&SwapChain, Error> { - self.0.swap_chain.as_ref().ok_or(Error::WidgetAttached) + fn gl_display(&self) -> NativeDisplay { + self.surfman_rendering_info.gl_display() } - pub fn resize(&self, size: Size2D) -> Result<(), Error> { - let device = &mut self.0.device.borrow_mut(); - let context = &mut self.0.context.borrow_mut(); - if let Some(swap_chain) = self.0.swap_chain.as_ref() { - return swap_chain.resize(device, context, size); - } - let mut surface = device.unbind_surface_from_context(context)?.unwrap(); - device.resize_surface(context, &mut surface, size)?; - device - .bind_surface_to_context(context, surface) - .map_err(|(err, mut surface)| { - let _ = device.destroy_surface(context, &mut surface); - err - }) + fn prepare_for_rendering(&self) { + self.surfman_rendering_info.prepare_for_rendering(); } - pub fn present(&self) -> Result<(), Error> { - let device = &mut self.0.device.borrow_mut(); - let context = &mut self.0.context.borrow_mut(); - if let Some(ref swap_chain) = self.0.swap_chain { - return swap_chain.swap_buffers(device, context, PreserveBuffer::No); - } - let mut surface = device.unbind_surface_from_context(context)?.unwrap(); - device.present_surface(context, &mut surface)?; - device - .bind_surface_to_context(context, surface) - .map_err(|(err, mut surface)| { - let _ = device.destroy_surface(context, &mut surface); - err - }) + fn read_to_image(&self, source_rectangle: Rect) -> Option { + self.surfman_rendering_info.read_to_image(source_rectangle) } - /// Invoke a closure with the surface associated with the current front buffer. - /// This can be used to create a surfman::SurfaceTexture to blit elsewhere. - pub fn with_front_buffer Surface>(&self, f: F) { - let device = &mut self.0.device.borrow_mut(); - let context = &mut self.0.context.borrow_mut(); - let surface = device - .unbind_surface_from_context(context) - .unwrap() - .unwrap(); - let surface = f(device, surface); - device.bind_surface_to_context(context, surface).unwrap(); + fn resize(&self, size: Size2D) { + let device = &mut self.surfman_rendering_info.device.borrow_mut(); + let context = &mut self.surfman_rendering_info.context.borrow_mut(); + let _ = self.swap_chain.resize(device, context, size); } - pub fn device(&self) -> std::cell::Ref { - self.0.device.borrow() + fn present(&self) { + let device = &mut self.surfman_rendering_info.device.borrow_mut(); + let context = &mut self.surfman_rendering_info.context.borrow_mut(); + let _ = self + .swap_chain + .swap_buffers(device, context, PreserveBuffer::No); } - pub fn connection(&self) -> Connection { - let device = &self.0.device.borrow(); - device.connection() + fn make_current(&self) -> Result<(), Error> { + self.surfman_rendering_info.make_current() } - pub fn adapter(&self) -> Adapter { - let device = &self.0.device.borrow(); - device.adapter() + #[allow(unsafe_code)] + fn gl_api(&self) -> Rc { + self.surfman_rendering_info.gl.clone() } - pub fn native_context(&self) -> NativeContext { - let device = &self.0.device.borrow(); - let context = &self.0.context.borrow(); - device.native_context(context) + fn gl_version(&self) -> GLVersion { + self.surfman_rendering_info.gl_version() } - pub fn native_device(&self) -> NativeDevice { - let device = &self.0.device.borrow(); - device.native_device() + fn create_texture(&self, surface: Surface) -> Option<(SurfaceTexture, u32, Size2D)> { + self.surfman_rendering_info.create_texture(surface) } - pub fn context_attributes(&self) -> ContextAttributes { - let device = &self.0.device.borrow(); - let context = &self.0.context.borrow(); - let descriptor = &device.context_descriptor(context); - device.context_descriptor_attributes(descriptor) + fn destroy_texture(&self, surface_texture: SurfaceTexture) -> Option { + self.surfman_rendering_info.destroy_texture(surface_texture) } - pub fn context_surface_info(&self) -> Result, Error> { - let device = &self.0.device.borrow(); - let context = &self.0.context.borrow(); - device.context_surface_info(context) + fn connection(&self) -> Option { + self.surfman_rendering_info.connection() + } +} + +/// A [`RenderingContext`] that uses the `surfman` library to render to a +/// `raw-window-handle` identified window. `surfman` will attempt to create an +/// OpenGL context and surface for this window. This is a simple implementation +/// of the [`RenderingContext`] crate, but by default it paints to the entire window +/// surface. +/// +/// If you would like to paint to only a portion of the window, consider using +/// [`OffscreenRenderingContext`] by calling [`WindowRenderingContext::offscreen_context`]. +pub struct WindowRenderingContext(SurfmanRenderingContext); + +impl WindowRenderingContext { + pub fn new( + display_handle: DisplayHandle, + window_handle: WindowHandle, + size: &PhysicalSize, + ) -> Result { + let connection = Connection::from_display_handle(display_handle)?; + let adapter = connection.create_adapter()?; + let surfman_rendering_info = SurfmanRenderingContext::new(&connection, &adapter)?; + + let native_widget = connection + .create_native_widget_from_window_handle( + window_handle, + Size2D::new(size.width as i32, size.height as i32), + ) + .expect("Failed to create native widget"); + + let surface = + surfman_rendering_info.create_surface(SurfaceType::Widget { native_widget })?; + surfman_rendering_info.bind_surface(surface)?; + surfman_rendering_info.make_current()?; + + Ok(Self(surfman_rendering_info)) } - pub fn surface_info(&self, surface: &Surface) -> SurfaceInfo { - let device = &self.0.device.borrow(); - device.surface_info(surface) - } - - pub fn surface_texture_object(&self, surface: &SurfaceTexture) -> u32 { - let device = &self.0.device.borrow(); - device - .surface_texture_object(surface) - .map(|t| t.0.get()) - .unwrap_or_default() + pub fn offscreen_context(self: &Rc, size: Size2D) -> OffscreenRenderingContext { + OffscreenRenderingContext::new(self.clone(), size) } + /// TODO: This can be removed when Servo switches fully to `glow.` pub fn get_proc_address(&self, name: &str) -> *const c_void { let device = &self.0.device.borrow(); let context = &self.0.context.borrow(); device.get_proc_address(context, name) } - pub fn unbind_native_surface_from_context(&self) -> Result<(), Error> { + /// Stop rendering to the window that was used to create this `WindowRenderingContext` + /// or last set with [`Self::set_window`]. + /// + /// TODO: This should be removed once `WebView`s can replace their `RenderingContext`s. + pub fn take_window(&self) -> Result<(), Error> { let device = self.0.device.borrow_mut(); let mut context = self.0.context.borrow_mut(); let mut surface = device.unbind_surface_from_context(&mut context)?.unwrap(); @@ -481,12 +511,30 @@ impl SurfmanRenderingContext { Ok(()) } - pub fn bind_native_surface_to_context(&self, native_widget: NativeWidget) -> Result<(), Error> { + /// Replace the window that this [`WindowRenderingContext`] renders to and give it a new + /// size. + /// + /// TODO: This should be removed once `WebView`s can replace their `RenderingContext`s. + pub fn set_window( + &self, + window_handle: WindowHandle, + size: &PhysicalSize, + ) -> Result<(), Error> { let mut device = self.0.device.borrow_mut(); let mut context = self.0.context.borrow_mut(); + + let native_widget = device + .connection() + .create_native_widget_from_window_handle( + window_handle, + Size2D::new(size.width as i32, size.height as i32), + ) + .expect("Failed to create native widget"); + let surface_access = SurfaceAccess::GPUOnly; let surface_type = SurfaceType::Widget { native_widget }; let surface = device.create_surface(&context, surface_access, surface_type)?; + device .bind_surface_to_context(&mut context, surface) .map_err(|(err, mut surface)| { @@ -496,18 +544,60 @@ impl SurfmanRenderingContext { device.make_context_current(&context)?; Ok(()) } +} - pub fn framebuffer(&self) -> Option { - self.context_surface_info() - .unwrap_or(None) - .and_then(|info| info.framebuffer_object) +impl RenderingContext for WindowRenderingContext { + fn gl_context(&self) -> GlContext { + self.0.gl_context() } - /// Create a new offscreen context that is compatible with this [`SurfmanRenderingContext`]. - /// The contents of the resulting [`OffscreenRenderingContext`] are guaranteed to be blit - /// compatible with the this context. - pub fn offscreen_context(&self, size: Size2D) -> OffscreenRenderingContext { - OffscreenRenderingContext::new(SurfmanRenderingContext(self.0.clone()), size) + fn gl_display(&self) -> NativeDisplay { + self.0.gl_display() + } + + fn prepare_for_rendering(&self) { + self.0.prepare_for_rendering(); + } + + fn read_to_image(&self, source_rectangle: Rect) -> Option { + self.0.read_to_image(source_rectangle) + } + + fn resize(&self, size: Size2D) { + if let Err(error) = self.0.resize_surface(size) { + warn!("Error resizing surface: {error:?}"); + } + } + + fn present(&self) { + if let Err(error) = self.0.present_bound_surface() { + warn!("Error presenting surface: {error:?}"); + } + } + + fn make_current(&self) -> Result<(), Error> { + self.0.make_current() + } + + #[allow(unsafe_code)] + fn gl_api(&self) -> Rc { + self.0.gl.clone() + } + + fn gl_version(&self) -> GLVersion { + self.0.gl_version() + } + + fn create_texture(&self, surface: Surface) -> Option<(SurfaceTexture, u32, Size2D)> { + self.0.create_texture(surface) + } + + fn destroy_texture(&self, surface_texture: SurfaceTexture) -> Option { + self.0.destroy_texture(surface_texture) + } + + fn connection(&self) -> Option { + self.0.connection() } } @@ -605,11 +695,11 @@ impl Framebuffer { } fn read_to_image(&self, source_rectangle: Rect) -> Option { - Self::read_framebuffer_to_image(self.gl.clone(), self.framebuffer_id, source_rectangle) + Self::read_framebuffer_to_image(&self.gl, self.framebuffer_id, source_rectangle) } fn read_framebuffer_to_image( - gl: Rc, + gl: &Rc, framebuffer_id: u32, source_rectangle: Rect, ) -> Option { @@ -656,7 +746,7 @@ impl Framebuffer { } pub struct OffscreenRenderingContext { - parent_context: SurfmanRenderingContext, + parent_context: Rc, size: Cell>, back_framebuffer: RefCell, front_framebuffer: RefCell>, @@ -665,7 +755,7 @@ pub struct OffscreenRenderingContext { type RenderToParentCallback = Box) + Send + Sync>; impl OffscreenRenderingContext { - fn new(parent_context: SurfmanRenderingContext, size: Size2D) -> Self { + fn new(parent_context: Rc, size: Size2D) -> Self { let next_framebuffer = Framebuffer::new(parent_context.gl_api(), size); Self { parent_context, @@ -675,7 +765,7 @@ impl OffscreenRenderingContext { } } - pub fn parent_context(&self) -> &SurfmanRenderingContext { + pub fn parent_context(&self) -> &WindowRenderingContext { &self.parent_context } @@ -690,7 +780,7 @@ impl OffscreenRenderingContext { // Don't accept a `None` context for the read framebuffer. let front_framebuffer_id = NonZeroU32::new(self.front_framebuffer_id()?).map(NativeFramebuffer)?; - let parent_context_framebuffer_id = self.parent_context.framebuffer(); + let parent_context_framebuffer_id = self.parent_context.0.framebuffer(); let size = self.size.get(); Some(Box::new(move |gl, target_rect| { Self::render_framebuffer_to_parent_context( @@ -776,7 +866,7 @@ impl RenderingContext for OffscreenRenderingContext { } fn make_current(&self) -> Result<(), surfman::Error> { - self.parent_context.make_gl_context_current() + self.parent_context.make_current() } fn gl_api(&self) -> Rc { @@ -796,7 +886,7 @@ impl RenderingContext for OffscreenRenderingContext { } fn connection(&self) -> Option { - Some(self.parent_context.connection()) + self.parent_context.connection() } fn read_to_image(&self, source_rectangle: Rect) -> Option { diff --git a/ports/servoshell/Cargo.toml b/ports/servoshell/Cargo.toml index 7a8061f9851..152cbe1f854 100644 --- a/ports/servoshell/Cargo.toml +++ b/ports/servoshell/Cargo.toml @@ -57,6 +57,7 @@ webxr = ["libservo/webxr"] webgpu = ["libservo/webgpu"] [dependencies] +dpi = { workspace = true } euclid = { workspace = true } libc = { workspace = true } libservo = { path = "../../components/servo", features = ["background_hang_monitor", "bluetooth"] } @@ -67,6 +68,7 @@ getopts = { workspace = true } hitrace = { workspace = true, optional = true } mime_guess = { workspace = true } url = { workspace = true } +raw-window-handle = { workspace = true } rustls = { workspace = true, features = ["aws-lc-rs"] } tokio = { workspace = true } tracing = { workspace = true, optional = true } @@ -116,7 +118,6 @@ headers = { workspace = true } http = { workspace = true } net = { path = "../../components/net" } net_traits = { workspace = true } -raw-window-handle = "0.6" serde_json = { workspace = true } shellwords = "1.0.0" surfman = { workspace = true, features = ["sm-x11", "sm-raw-window-handle-06"] } diff --git a/ports/servoshell/desktop/headed_window.rs b/ports/servoshell/desktop/headed_window.rs index aeefe56b16b..68dfc519888 100644 --- a/ports/servoshell/desktop/headed_window.rs +++ b/ports/servoshell/desktop/headed_window.rs @@ -12,7 +12,7 @@ use std::time::Duration; use euclid::{Angle, Length, Point2D, Rotation3D, Scale, Size2D, UnknownUnit, Vector2D, Vector3D}; use keyboard_types::{Modifiers, ShortcutMatcher}; -use log::{debug, info, warn}; +use log::{debug, info}; use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use servo::compositing::windowing::{ AnimationState, EmbedderCoordinates, WebRenderDebugOption, WindowMethods, @@ -22,14 +22,13 @@ use servo::servo_config::pref; use servo::servo_geometry::DeviceIndependentPixel; use servo::webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixel}; use servo::webrender_api::ScrollLocation; -use servo::webrender_traits::rendering_context::{OffscreenRenderingContext, RenderingContext}; -use servo::webrender_traits::SurfmanRenderingContext; use servo::{ Cursor, InputEvent, Key, KeyState, KeyboardEvent, MouseButton as ServoMouseButton, - MouseButtonAction, MouseButtonEvent, MouseMoveEvent, Theme, TouchAction, TouchEvent, - TouchEventType, TouchId, WebView, WheelDelta, WheelEvent, WheelMode, + MouseButtonAction, MouseButtonEvent, MouseMoveEvent, OffscreenRenderingContext, + RenderingContext, Theme, TouchAction, TouchEvent, TouchEventType, TouchId, WebView, WheelDelta, + WheelEvent, WheelMode, WindowRenderingContext, }; -use surfman::{Connection, Context, Device, SurfaceType}; +use surfman::{Context, Device}; use url::Url; use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize}; use winit::event::{ @@ -69,7 +68,7 @@ pub struct Window { /// The RenderingContext that renders directly onto the Window. This is used as /// the target of egui rendering and also where Servo rendering results are finally /// blitted. - window_rendering_context: SurfmanRenderingContext, + window_rendering_context: Rc, /// The `RenderingContext` of Servo itself. This is used to render Servo results /// temporarily until they can be blitted into the egui scene. @@ -125,32 +124,16 @@ impl Window { let display_handle = event_loop .display_handle() .expect("could not get display handle from window"); - let connection = - Connection::from_display_handle(display_handle).expect("Failed to create connection"); - let adapter = connection - .create_adapter() - .expect("Failed to create adapter"); let window_handle = winit_window .window_handle() .expect("could not get window handle from window"); - let native_widget = connection - .create_native_widget_from_window_handle( - window_handle, - winit_size_to_euclid_size(inner_size).to_i32().to_untyped(), - ) - .expect("Failed to create native widget"); - - let window_rendering_context = SurfmanRenderingContext::create(&connection, &adapter, None) - .expect("Failed to create window RenderingContext"); - let surface = window_rendering_context - .create_surface(SurfaceType::Widget { native_widget }) - .expect("Failed to create surface"); - window_rendering_context - .bind_surface(surface) - .expect("Failed to bind surface"); + let window_rendering_context = Rc::new( + WindowRenderingContext::new(display_handle, window_handle, &inner_size) + .expect("Could not create RenderingContext for Window"), + ); // Make sure the gl context is made current. - window_rendering_context.make_gl_context_current().unwrap(); + window_rendering_context.make_current().unwrap(); let rendering_context_size = Size2D::new(inner_size.width, inner_size.height); let rendering_context = @@ -633,13 +616,8 @@ impl WindowPortsMethods for Window { WindowEvent::Resized(new_size) => { if self.inner_size.get() != new_size { let rendering_context_size = Size2D::new(new_size.width, new_size.height); - if let Err(error) = self - .window_rendering_context - .resize(rendering_context_size.to_i32()) - { - warn!("Could not resize window RenderingContext: {error:?}"); - } - + self.window_rendering_context + .resize(rendering_context_size.to_i32()); self.inner_size.set(new_size); webview.notify_rendering_context_resized(); } diff --git a/ports/servoshell/desktop/headless_window.rs b/ports/servoshell/desktop/headless_window.rs index 5fa341b326a..7c2c7170a04 100644 --- a/ports/servoshell/desktop/headless_window.rs +++ b/ports/servoshell/desktop/headless_window.rs @@ -12,9 +12,8 @@ use euclid::{Box2D, Length, Point2D, Scale, Size2D}; use servo::compositing::windowing::{AnimationState, EmbedderCoordinates, WindowMethods}; use servo::servo_geometry::DeviceIndependentPixel; use servo::webrender_api::units::{DeviceIntSize, DevicePixel}; -use servo::webrender_traits::rendering_context::RenderingContext; -use servo::webrender_traits::SurfmanRenderingContext; -use surfman::Connection; +use servo::{RenderingContext, SoftwareRenderingContext}; +use winit::dpi::PhysicalSize; use super::app_state::RunningAppState; use crate::desktop::window_trait::WindowPortsMethods; @@ -27,30 +26,24 @@ pub struct Window { inner_size: Cell, screen_size: Size2D, window_rect: Box2D, - rendering_context: SurfmanRenderingContext, + rendering_context: Rc, } impl Window { #[allow(clippy::new_ret_no_self)] pub fn new(servoshell_preferences: &ServoShellPreferences) -> Rc { let size = servoshell_preferences.initial_window_size; - let connection = Connection::new().expect("Failed to create connection"); - let adapter = connection - .create_software_adapter() - .expect("Failed to create adapter"); - let rendering_context = SurfmanRenderingContext::create( - &connection, - &adapter, - Some(size.to_untyped().to_i32()), - ) - .expect("Failed to create WR surfman"); let device_pixel_ratio_override = servoshell_preferences.device_pixel_ratio_override; let device_pixel_ratio_override: Option> = device_pixel_ratio_override.map(Scale::new); let hidpi_factor = device_pixel_ratio_override.unwrap_or_else(Scale::identity); - let inner_size = Cell::new((size.to_f32() * hidpi_factor).to_i32()); + let inner_size = (size.to_f32() * hidpi_factor).to_i32(); + let physical_size = PhysicalSize::new(inner_size.width as u32, inner_size.height as u32); + let rendering_context = + SoftwareRenderingContext::new(physical_size).expect("Failed to create WR surfman"); + let window_rect = Box2D::from_origin_and_size(Point2D::zero(), size.to_i32()); let screen_size = servoshell_preferences.screen_size_override.map_or_else( @@ -62,10 +55,10 @@ impl Window { animation_state: Cell::new(AnimationState::Idle), fullscreen: Cell::new(false), device_pixel_ratio_override, - inner_size, + inner_size: Cell::new(inner_size), screen_size, window_rect, - rendering_context, + rendering_context: Rc::new(rendering_context), }; Rc::new(window) @@ -150,9 +143,7 @@ impl WindowPortsMethods for Window { } fn rendering_context(&self) -> Rc { - // `SurfmanRenderingContext` uses shared ownership internally so cloning it here does - // not create a new one really. - Rc::new(self.rendering_context.clone()) + self.rendering_context.clone() } } diff --git a/ports/servoshell/desktop/minibrowser.rs b/ports/servoshell/desktop/minibrowser.rs index b9ae9966a5a..bc5cb9710cb 100644 --- a/ports/servoshell/desktop/minibrowser.rs +++ b/ports/servoshell/desktop/minibrowser.rs @@ -21,8 +21,7 @@ use servo::base::id::WebViewId; use servo::servo_geometry::DeviceIndependentPixel; use servo::servo_url::ServoUrl; use servo::webrender_api::units::DevicePixel; -use servo::webrender_traits::rendering_context::{OffscreenRenderingContext, RenderingContext}; -use servo::{LoadStatus, WebView}; +use servo::{LoadStatus, OffscreenRenderingContext, RenderingContext, WebView}; use winit::event::{ElementState, MouseButton, WindowEvent}; use winit::event_loop::ActiveEventLoop; use winit::window::Window; @@ -429,7 +428,7 @@ impl Minibrowser { .parent_context() .prepare_for_rendering(); self.context.paint(window); - let _ = self.rendering_context.parent_context().present(); + self.rendering_context.parent_context().present(); } /// Updates the location field from the given [WebViewManager], unless the user has started diff --git a/ports/servoshell/desktop/window_trait.rs b/ports/servoshell/desktop/window_trait.rs index 934a5d32990..915cd502eb4 100644 --- a/ports/servoshell/desktop/window_trait.rs +++ b/ports/servoshell/desktop/window_trait.rs @@ -11,8 +11,7 @@ use euclid::{Length, Scale}; use servo::compositing::windowing::WindowMethods; use servo::servo_geometry::DeviceIndependentPixel; use servo::webrender_api::units::{DeviceIntPoint, DeviceIntSize, DevicePixel}; -use servo::webrender_traits::rendering_context::RenderingContext; -use servo::{Cursor, WebView}; +use servo::{Cursor, RenderingContext, WebView}; use super::app_state::RunningAppState; diff --git a/ports/servoshell/egl/android.rs b/ports/servoshell/egl/android.rs index 3b9721fb1f5..84b511b3146 100644 --- a/ports/servoshell/egl/android.rs +++ b/ports/servoshell/egl/android.rs @@ -8,6 +8,7 @@ mod resources; mod simpleservo; use std::os::raw::{c_char, c_int, c_void}; +use std::ptr::NonNull; use std::sync::Arc; use android_logger::{self, Config, FilterBuilder}; @@ -15,6 +16,9 @@ use jni::objects::{GlobalRef, JClass, JObject, JString, JValue, JValueOwned}; use jni::sys::{jboolean, jfloat, jint, jobject}; use jni::{JNIEnv, JavaVM}; use log::{debug, error, info, warn}; +use raw_window_handle::{ + AndroidDisplayHandle, AndroidNdkWindowHandle, RawDisplayHandle, RawWindowHandle, +}; use servo::{LoadStatus, MediaSessionActionType}; use simpleservo::{ DeviceIntRect, EventLoopWaker, InitOptions, InputMethodType, MediaSessionPlaybackState, @@ -391,12 +395,15 @@ pub extern "C" fn Java_org_servo_servoview_JNIServo_resumeCompositor<'local>( coordinates: JObject<'local>, ) { debug!("resumeCompositor"); - let widget = unsafe { ANativeWindow_fromSurface(env.get_native_interface(), surface.as_raw()) }; - let coords = jni_coords_to_rust_coords(&mut env, &coordinates); - match coords { - Ok(coords) => call(&mut env, |s| s.resume_compositor(widget, coords.clone())), - Err(error) => throw(&mut env, &error), - } + let coords = match jni_coords_to_rust_coords(&mut env, &coordinates) { + Ok(coords) => coords, + Err(error) => return throw(&mut env, &error), + }; + + let (_, window_handle) = display_and_window_handle(&mut env, &surface); + call(&mut env, |s| { + s.resume_compositor(window_handle, coords.clone()) + }); } #[no_mangle] @@ -795,17 +802,29 @@ fn get_options<'local>( None => None, }; - let native_window = - unsafe { ANativeWindow_fromSurface(env.get_native_interface(), surface.as_raw()) }; - + let (display_handle, window_handle) = display_and_window_handle(env, surface); let opts = InitOptions { args: args.unwrap_or(vec![]), url, coordinates, density, xr_discovery: None, - surfman_integration: simpleservo::SurfmanIntegration::Widget(native_window), + window_handle, + display_handle, }; Ok((opts, log, log_str, gst_debug_str)) } + +fn display_and_window_handle( + env: &mut JNIEnv<'_>, + surface: &JObject<'_>, +) -> (RawDisplayHandle, RawWindowHandle) { + let native_window = + unsafe { ANativeWindow_fromSurface(env.get_native_interface(), surface.as_raw()) }; + let native_window = NonNull::new(native_window).expect("Could not get Android window"); + ( + RawDisplayHandle::Android(AndroidDisplayHandle::new()), + RawWindowHandle::AndroidNdk(AndroidNdkWindowHandle::new(native_window)), + ) +} diff --git a/ports/servoshell/egl/android/simpleservo.rs b/ports/servoshell/egl/android/simpleservo.rs index 048b2e84e0e..53afb05f80b 100644 --- a/ports/servoshell/egl/android/simpleservo.rs +++ b/ports/servoshell/egl/android/simpleservo.rs @@ -4,19 +4,17 @@ use std::cell::RefCell; use std::mem; -use std::os::raw::c_void; use std::rc::Rc; +use raw_window_handle::{DisplayHandle, RawDisplayHandle, RawWindowHandle, WindowHandle}; use servo::compositing::CompositeTarget; pub use servo::webrender_api::units::DeviceIntRect; -use servo::webrender_traits::SurfmanRenderingContext; /// The EventLoopWaker::wake function will be called from any thread. /// It will be called to notify embedder that some events are available, /// and that perform_updates need to be called pub use servo::EventLoopWaker; use servo::{self, resources, Servo}; -pub use servo::{InputMethodType, MediaSessionPlaybackState, PromptResult}; -use surfman::{Connection, SurfaceType}; +pub use servo::{InputMethodType, MediaSessionPlaybackState, PromptResult, WindowRenderingContext}; use crate::egl::android::resources::ResourceReaderInstance; use crate::egl::app_state::{ @@ -36,13 +34,8 @@ pub struct InitOptions { pub density: f32, #[cfg(feature = "webxr")] pub xr_discovery: Option, - pub surfman_integration: SurfmanIntegration, -} - -/// Controls how this embedding's rendering will integrate with the embedder. -pub enum SurfmanIntegration { - /// Render directly to a provided native widget (see surfman::NativeWidget). - Widget(*mut c_void), + pub window_handle: RawWindowHandle, + pub display_handle: RawDisplayHandle, } /// Initialize Servo. At that point, we need a valid GL context. @@ -70,30 +63,20 @@ pub fn init( crate::init_tracing(servoshell_preferences.tracing_filter.as_deref()); - // Initialize surfman - let connection = Connection::new().or(Err("Failed to create connection"))?; - let adapter = connection - .create_adapter() - .or(Err("Failed to create adapter"))?; - let surface_type = match init_opts.surfman_integration { - SurfmanIntegration::Widget(native_widget) => { - let native_widget = unsafe { - connection.create_native_widget_from_ptr( - native_widget, - init_opts.coordinates.framebuffer.to_untyped(), - ) - }; - SurfaceType::Widget { native_widget } - }, + let (display_handle, window_handle) = unsafe { + ( + DisplayHandle::borrow_raw(init_opts.display_handle), + WindowHandle::borrow_raw(init_opts.window_handle), + ) }; - let rendering_context = SurfmanRenderingContext::create(&connection, &adapter, None) - .or(Err("Failed to create surface manager"))?; - let surface = rendering_context - .create_surface(surface_type) - .or(Err("Failed to create surface"))?; - rendering_context - .bind_surface(surface) - .or(Err("Failed to bind surface"))?; + let rendering_context = Rc::new( + WindowRenderingContext::new( + display_handle, + window_handle, + &init_opts.coordinates.framebuffer_size(), + ) + .expect("Could not create RenderingContext"), + ); let window_callbacks = Rc::new(ServoWindowCallbacks::new( callbacks, @@ -110,7 +93,7 @@ pub fn init( let servo = Servo::new( opts, preferences, - Rc::new(rendering_context.clone()), + rendering_context.clone(), embedder_callbacks, window_callbacks.clone(), None, diff --git a/ports/servoshell/egl/app_state.rs b/ports/servoshell/egl/app_state.rs index 085c931cfb3..651a593a22c 100644 --- a/ports/servoshell/egl/app_state.rs +++ b/ports/servoshell/egl/app_state.rs @@ -3,12 +3,13 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::cell::{Ref, RefCell, RefMut}; use std::collections::HashMap; -use std::os::raw::c_void; use std::rc::Rc; +use dpi::PhysicalSize; use ipc_channel::ipc::IpcSender; use keyboard_types::{CompositionEvent, CompositionState}; use log::{debug, error, info, warn}; +use raw_window_handle::{RawWindowHandle, WindowHandle}; use servo::base::id::WebViewId; use servo::compositing::windowing::{ AnimationState, EmbedderCoordinates, EmbedderMethods, WindowMethods, @@ -17,14 +18,13 @@ use servo::euclid::{Box2D, Point2D, Rect, Scale, Size2D, Vector2D}; use servo::servo_geometry::DeviceIndependentPixel; use servo::webrender_api::units::{DeviceIntRect, DeviceIntSize, DevicePixel, DeviceRect}; use servo::webrender_api::ScrollLocation; -use servo::webrender_traits::SurfmanRenderingContext; use servo::{ AllowOrDenyRequest, ContextMenuResult, EmbedderProxy, EventLoopWaker, ImeEvent, InputEvent, InputMethodType, Key, KeyState, KeyboardEvent, LoadStatus, MediaSessionActionType, MediaSessionEvent, MouseButton, MouseButtonAction, MouseButtonEvent, MouseMoveEvent, - NavigationRequest, PermissionRequest, PromptDefinition, PromptOrigin, PromptResult, Servo, - ServoDelegate, ServoError, TouchAction, TouchEvent, TouchEventType, TouchId, WebView, - WebViewDelegate, + NavigationRequest, PermissionRequest, PromptDefinition, PromptOrigin, PromptResult, + RenderingContext, Servo, ServoDelegate, ServoError, TouchAction, TouchEvent, TouchEventType, + TouchId, WebView, WebViewDelegate, WindowRenderingContext, }; use url::Url; @@ -51,6 +51,13 @@ impl Coordinates { framebuffer: Size2D::new(fb_width, fb_height), } } + + pub(crate) fn framebuffer_size(&self) -> PhysicalSize { + PhysicalSize::new( + self.framebuffer.width as u32, + self.framebuffer.height as u32, + ) + } } pub(super) struct ServoWindowCallbacks { @@ -75,7 +82,7 @@ impl ServoWindowCallbacks { pub struct RunningAppState { servo: Servo, - rendering_context: SurfmanRenderingContext, + rendering_context: Rc, callbacks: Rc, inner: RefCell, /// servoshell specific preferences created during startup of the application. @@ -283,7 +290,7 @@ impl WebViewDelegate for RunningAppState { impl RunningAppState { pub(super) fn new( initial_url: Option, - rendering_context: SurfmanRenderingContext, + rendering_context: Rc, servo: Servo, callbacks: Rc, servoshell_preferences: ServoShellPreferences, @@ -367,12 +374,6 @@ impl RunningAppState { self.servo.deinit(); } - /// Returns the webrender surface management integration interface. - /// This provides the embedder access to the current front buffer. - pub fn surfman(&self) -> SurfmanRenderingContext { - self.rendering_context.clone() - } - /// This is the Servo heartbeat. This needs to be called /// everytime wakeup is called or when embedder wants Servo /// to act on its pending events. @@ -429,10 +430,8 @@ impl RunningAppState { pub fn resize(&self, coordinates: Coordinates) { info!("resize to {:?}", coordinates); let size = coordinates.viewport.size; - let _ = self - .rendering_context - .resize(Size2D::new(size.width, size.height)) - .inspect_err(|e| error!("Failed to resize rendering context: {e:?}")); + self.rendering_context + .resize(Size2D::new(size.width, size.height)); *self.callbacks.coordinates.borrow_mut() = coordinates; self.active_webview().notify_rendering_context_resized(); self.active_webview() @@ -632,24 +631,17 @@ impl RunningAppState { } pub fn pause_compositor(&self) { - if let Err(e) = self.rendering_context.unbind_native_surface_from_context() { + if let Err(e) = self.rendering_context.take_window() { warn!("Unbinding native surface from context failed ({:?})", e); } self.perform_updates(); } - pub fn resume_compositor(&self, native_surface: *mut c_void, coords: Coordinates) { - if native_surface.is_null() { - panic!("null passed for native_surface"); - } - let connection = self.rendering_context.connection(); - let native_widget = unsafe { - connection - .create_native_widget_from_ptr(native_surface, coords.framebuffer.to_untyped()) - }; + pub fn resume_compositor(&self, window_handle: RawWindowHandle, coords: Coordinates) { + let window_handle = unsafe { WindowHandle::borrow_raw(window_handle) }; if let Err(e) = self .rendering_context - .bind_native_surface_to_context(native_widget) + .set_window(window_handle, &coords.framebuffer_size()) { warn!("Binding native surface to context failed ({:?})", e); } @@ -688,6 +680,7 @@ impl RunningAppState { pub fn present_if_needed(&self) { if self.inner().need_present { self.inner_mut().need_present = false; + self.active_webview().paint_immediately(); self.servo.present(); } } diff --git a/ports/servoshell/egl/ohos/simpleservo.rs b/ports/servoshell/egl/ohos/simpleservo.rs index 723cd4c6105..2b69ce952b4 100644 --- a/ports/servoshell/egl/ohos/simpleservo.rs +++ b/ports/servoshell/egl/ohos/simpleservo.rs @@ -2,20 +2,22 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::cell::RefCell; -use std::convert::TryInto; use std::os::raw::c_void; use std::path::PathBuf; +use std::ptr::NonNull; use std::rc::Rc; use log::{debug, info}; +use raw_window_handle::{ + DisplayHandle, OhosDisplayHandle, OhosNdkWindowHandle, RawDisplayHandle, RawWindowHandle, + WindowHandle, +}; use servo::compositing::CompositeTarget; -use servo::webrender_traits::SurfmanRenderingContext; /// The EventLoopWaker::wake function will be called from any thread. /// It will be called to notify embedder that some events are available, /// and that perform_updates need to be called pub use servo::EventLoopWaker; -use servo::{self, resources, Servo}; -use surfman::{Connection, SurfaceType}; +use servo::{self, resources, Servo, WindowRenderingContext}; use xcomponent_sys::OH_NativeXComponent; use crate::egl::app_state::{ @@ -62,43 +64,39 @@ pub fn init( crate::init_tracing(servoshell_preferences.tracing_filter.as_deref()); - // Initialize surfman - let connection = Connection::new().or(Err("Failed to create connection"))?; - let adapter = connection - .create_adapter() - .or(Err("Failed to create adapter"))?; - let Ok(window_size) = (unsafe { super::get_xcomponent_size(xcomponent, native_window) }) else { return Err("Failed to get xcomponent size"); }; + let coordinates = Coordinates::new( + 0, + 0, + window_size.width, + window_size.height, + window_size.width, + window_size.height, + ); - debug!("Creating surfman widget with {window_size:?}"); - let native_widget = - unsafe { connection.create_native_widget_from_ptr(native_window, window_size) }; - let surface_type = SurfaceType::Widget { native_widget }; + let display_handle = RawDisplayHandle::Ohos(OhosDisplayHandle::new()); + let display_handle = unsafe { DisplayHandle::borrow_raw(display_handle) }; - info!("Creating rendering context"); - let rendering_context = SurfmanRenderingContext::create(&connection, &adapter, None) - .or(Err("Failed to create surface manager"))?; - let surface = rendering_context - .create_surface(surface_type) - .or(Err("Failed to create surface"))?; - rendering_context - .bind_surface(surface) - .or(Err("Failed to bind surface"))?; + let native_window = NonNull::new(native_window).expect("Could not get native window"); + let window_handle = RawWindowHandle::OhosNdk(OhosNdkWindowHandle::new(native_window)); + let window_handle = unsafe { WindowHandle::borrow_raw(window_handle) }; + + let rendering_context = Rc::new( + WindowRenderingContext::new( + display_handle, + window_handle, + &coordinates.framebuffer_size(), + ) + .expect("Could not create RenderingContext"), + ); info!("before ServoWindowCallbacks..."); let window_callbacks = Rc::new(ServoWindowCallbacks::new( callbacks, - RefCell::new(Coordinates::new( - 0, - 0, - window_size.width, - window_size.height, - window_size.width, - window_size.height, - )), + RefCell::new(coordinates), options.display_density as f32, )); @@ -111,7 +109,7 @@ pub fn init( let servo = Servo::new( opts, preferences, - Rc::new(rendering_context.clone()), + rendering_context.clone(), embedder_callbacks, window_callbacks.clone(), None, /* user_agent */