libservo: Expose SoftwareRenderingContext and WindowRenderingContext (#35501)

Expose two easy-to-use wrappers around `SurfmanRenderingContext` that
make the API simpler to use:

- `WindowRenderingContext`: This `RenderingContext` is a newtype around
  `SurfmanRenderingContext` takes a `raw-window-handle` display and window
  and creates a full window rendering context.
- `SoftwareRenderingContext`: is wraps `SurfmanRenderingContext` and
  adds a swap chain in order to expose a software GL rendering context.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson 2025-02-18 15:50:41 +01:00 committed by GitHub
parent 73507f58e6
commit f34f2d9d0a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 514 additions and 470 deletions

View file

@ -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<WindowRenderingContext>,
/// 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();
}

View file

@ -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<DeviceIntSize>,
screen_size: Size2D<i32, DeviceIndependentPixel>,
window_rect: Box2D<i32, DeviceIndependentPixel>,
rendering_context: SurfmanRenderingContext,
rendering_context: Rc<SoftwareRenderingContext>,
}
impl Window {
#[allow(clippy::new_ret_no_self)]
pub fn new(servoshell_preferences: &ServoShellPreferences) -> Rc<dyn WindowPortsMethods> {
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<Scale<f32, DeviceIndependentPixel, DevicePixel>> =
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<dyn RenderingContext> {
// `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()
}
}

View file

@ -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

View file

@ -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;