libservo: Move size handling to RenderContext from WindowMethods (#35621)

This is the first step toward removing `WindowMethods`, which will
gradually be integrated into the `WebView` and `WebViewDelegate`. Sizing
of the `WebView` is now handled by the a size associated with a
`RenderingContext`. `WebView`s will eventually just paint the entire
size of their `RenderingContext`. Notes:

- This is transitionary step so now there is a `WebView::resize` and a
  `WebView::move_resize`. The first is the future which will resize the
  `WebView` and its associated `RenderingContext`. The second is a
  function that the virtual `WebView`s that will soon be replaced by a
  the one-`WebView` per `WebView` model.
- We do not need to call `WebView::move_resize` at as much any longer
  because the default size of the `WebView` is to take up the whole
  `RenderingContext`.
- `SurfmanRenderingContext` is no longer exposed in the API, as a
  surfman context doesn't naturally have a size unless a surface is
  bound to it.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson 2025-02-25 16:03:53 +01:00 committed by GitHub
parent ebb19bcd60
commit 23524a5413
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 200 additions and 312 deletions

2
Cargo.lock generated
View file

@ -1038,6 +1038,7 @@ dependencies = [
"bitflags 2.8.0",
"compositing_traits",
"crossbeam-channel",
"dpi",
"embedder_traits",
"euclid",
"fnv",
@ -4342,6 +4343,7 @@ dependencies = [
"crossbeam-channel",
"devtools",
"devtools_traits",
"dpi",
"embedder_traits",
"env_logger 0.10.2",
"euclid",

View file

@ -22,6 +22,7 @@ base = { workspace = true }
bitflags = { workspace = true }
compositing_traits = { workspace = true }
crossbeam-channel = { workspace = true }
dpi = { workspace = true }
embedder_traits = { workspace = true }
euclid = { workspace = true }
fnv = { workspace = true }

View file

@ -20,11 +20,12 @@ use compositing_traits::{
CompositionPipeline, CompositorMsg, CompositorReceiver, ConstellationMsg, SendableFrameTree,
};
use crossbeam_channel::Sender;
use dpi::PhysicalSize;
use embedder_traits::{
Cursor, InputEvent, MouseButton, MouseButtonAction, MouseButtonEvent, MouseMoveEvent,
ShutdownState, TouchEvent, TouchEventType, TouchId,
};
use euclid::{Point2D, Rect, Scale, Size2D, Transform3D, Vector2D};
use euclid::{Box2D, Point2D, Rect, Scale, Size2D, Transform3D, Vector2D};
use fnv::{FnvHashMap, FnvHashSet};
use ipc_channel::ipc::{self, IpcSharedMemory};
use libc::c_void;
@ -41,7 +42,7 @@ use servo_geometry::DeviceIndependentPixel;
use style_traits::{CSSPixel, PinchZoomFactor};
use webrender::{CaptureBits, RenderApi, Transaction};
use webrender_api::units::{
DeviceIntPoint, DeviceIntSize, DevicePixel, DevicePoint, DeviceRect, LayoutPoint, LayoutRect,
DeviceIntPoint, DeviceIntRect, DevicePixel, DevicePoint, DeviceRect, LayoutPoint, LayoutRect,
LayoutSize, LayoutVector2D, WorldPoint,
};
use webrender_api::{
@ -946,10 +947,11 @@ impl IOCompositor {
);
let scaled_viewport_size =
self.embedder_coordinates.get_viewport().size().to_f32() / zoom_factor;
let scaled_viewport_size = LayoutSize::from_untyped(scaled_viewport_size.to_untyped());
let scaled_viewport_rect =
LayoutRect::from_origin_and_size(LayoutPoint::zero(), scaled_viewport_size);
self.rendering_context.size2d().to_f32().to_untyped() / zoom_factor;
let scaled_viewport_rect = LayoutRect::from_origin_and_size(
LayoutPoint::zero(),
LayoutSize::from_untyped(scaled_viewport_size),
);
let root_clip_id = builder.define_clip_rect(zoom_reference_frame, scaled_viewport_rect);
let clip_chain_id = builder.define_clip_chain(None, [root_clip_id]);
@ -1025,11 +1027,12 @@ impl IOCompositor {
"{:?}: Creating new webview with pipeline {:?}",
top_level_browsing_context_id, pipeline_id
);
let size = self.rendering_context.size2d().to_f32();
if let Err(WebViewAlreadyExists(webview_id)) = self.global.webviews.add(
top_level_browsing_context_id,
WebView {
pipeline_id,
rect: self.embedder_coordinates.get_viewport().to_f32(),
rect: Box2D::from_origin_and_size(Point2D::origin(), size),
},
) {
error!("{webview_id}: Creating webview that already exists");
@ -1235,31 +1238,31 @@ impl IOCompositor {
self.embedder_coordinates = self.window.get_coordinates();
}
pub fn on_rendering_context_resized(&mut self) -> bool {
pub fn resize_rendering_context(&mut self, new_size: PhysicalSize<u32>) -> bool {
if self.shutdown_state() != ShutdownState::NotShuttingDown {
return false;
}
let old_coords = self.embedder_coordinates;
let old_hidpi_factor = self.embedder_coordinates.hidpi_factor;
self.embedder_coordinates = self.window.get_coordinates();
if self.embedder_coordinates.viewport != old_coords.viewport {
let mut transaction = Transaction::new();
let size = self.embedder_coordinates.get_viewport();
transaction.set_document_view(size);
self.rendering_context.resize(size.size().to_untyped());
self.global
.webrender_api
.send_transaction(self.webrender_document, transaction);
}
// A size change could also mean a resolution change.
if self.embedder_coordinates.hidpi_factor == old_coords.hidpi_factor &&
self.embedder_coordinates.viewport == old_coords.viewport
if self.embedder_coordinates.hidpi_factor == old_hidpi_factor &&
self.rendering_context.size() == new_size
{
return false;
}
self.rendering_context.resize(new_size);
let mut transaction = Transaction::new();
let output_region = DeviceIntRect::new(
Point2D::zero(),
Point2D::new(new_size.width as i32, new_size.height as i32),
);
transaction.set_document_view(output_region);
self.global
.webrender_api
.send_transaction(self.webrender_document, transaction);
self.update_after_zoom_or_hidpi_change();
self.set_needs_repaint(RepaintReason::Resize);
true
@ -2085,28 +2088,25 @@ impl IOCompositor {
) -> Result<Option<Image>, UnableToComposite> {
self.render_inner()?;
let size = self.embedder_coordinates.framebuffer.to_u32();
let (x, y, width, height) = if let Some(rect) = page_rect {
let size = self.rendering_context.size2d().to_i32();
let rect = if let Some(rect) = page_rect {
let rect = self.device_pixels_per_page_pixel().transform_rect(&rect);
let x = rect.origin.x as i32;
// We need to convert to the bottom-left origin coordinate
// system used by OpenGL
let y = (size.height as f32 - rect.origin.y - rect.size.height) as i32;
let w = rect.size.width as u32;
let h = rect.size.height as u32;
let w = rect.size.width as i32;
let h = rect.size.height as i32;
(x, y, w, h)
DeviceIntRect::from_origin_and_size(Point2D::new(x, y), Size2D::new(w, h))
} else {
(0, 0, size.width, size.height)
DeviceIntRect::from_origin_and_size(Point2D::origin(), size)
};
Ok(self
.rendering_context
.read_to_image(Rect::new(
Point2D::new(x as u32, y as u32),
Size2D::new(width, height),
))
.read_to_image(rect)
.map(|image| Image {
width: image.width(),
height: image.height(),
@ -2155,13 +2155,11 @@ impl IOCompositor {
|| {
trace!("Compositing");
let size =
DeviceIntSize::from_untyped(self.embedder_coordinates.framebuffer.to_untyped());
// Paint the scene.
// TODO(gw): Take notice of any errors the renderer returns!
self.clear_background();
if let Some(webrender) = self.webrender.as_mut() {
let size = self.rendering_context.size2d().to_i32();
webrender.render(size, 0 /* buffer_age */).ok();
}
},

View file

@ -10,7 +10,7 @@ use embedder_traits::{EventLoopWaker, MouseButton};
use euclid::Scale;
use net::protocols::ProtocolRegistry;
use servo_geometry::{DeviceIndependentIntRect, DeviceIndependentIntSize, DeviceIndependentPixel};
use webrender_api::units::{DeviceIntRect, DeviceIntSize, DevicePixel, DevicePoint};
use webrender_api::units::{DevicePixel, DevicePoint};
#[derive(Clone)]
pub enum MouseWindowEvent {
@ -86,94 +86,4 @@ pub struct EmbedderCoordinates {
pub available_screen_size: DeviceIndependentIntSize,
/// Position and size of the native window.
pub window_rect: DeviceIndependentIntRect,
/// Size of the GL buffer in the window.
pub framebuffer: DeviceIntSize,
/// Coordinates of the document within the framebuffer.
pub viewport: DeviceIntRect,
}
impl EmbedderCoordinates {
/// Get the unflipped viewport rectangle for use with the WebRender API.
pub fn get_viewport(&self) -> DeviceIntRect {
self.viewport
}
/// Flip the given rect.
/// This should be used when drawing directly to the framebuffer with OpenGL commands.
pub fn flip_rect(&self, rect: &DeviceIntRect) -> DeviceIntRect {
let mut result = *rect;
let min_y = self.framebuffer.height - result.max.y;
let max_y = self.framebuffer.height - result.min.y;
result.min.y = min_y;
result.max.y = max_y;
result
}
/// Get the flipped viewport rectangle.
/// This should be used when drawing directly to the framebuffer with OpenGL commands.
pub fn get_flipped_viewport(&self) -> DeviceIntRect {
self.flip_rect(&self.get_viewport())
}
}
#[cfg(test)]
mod test {
use euclid::{Box2D, Point2D, Scale, Size2D};
use webrender_api::units::DeviceIntRect;
use super::EmbedderCoordinates;
#[test]
fn test() {
let screen_size = Size2D::new(1080, 720);
let viewport = Box2D::from_origin_and_size(Point2D::zero(), Size2D::new(800, 600));
let window_rect = Box2D::from_origin_and_size(Point2D::zero(), Size2D::new(800, 600));
let coordinates = EmbedderCoordinates {
hidpi_factor: Scale::new(1.),
screen_size,
available_screen_size: screen_size,
window_rect,
framebuffer: viewport.size(),
viewport,
};
// Check if viewport conversion is correct.
let viewport = DeviceIntRect::new(Point2D::new(0, 0), Point2D::new(800, 600));
assert_eq!(coordinates.get_viewport(), viewport);
assert_eq!(coordinates.get_flipped_viewport(), viewport);
// Check rects with different y positions inside the viewport.
let rect1 = DeviceIntRect::new(Point2D::new(0, 0), Point2D::new(800, 400));
let rect2 = DeviceIntRect::new(Point2D::new(0, 100), Point2D::new(800, 600));
let rect3 = DeviceIntRect::new(Point2D::new(0, 200), Point2D::new(800, 500));
assert_eq!(
coordinates.flip_rect(&rect1),
DeviceIntRect::new(Point2D::new(0, 200), Point2D::new(800, 600))
);
assert_eq!(
coordinates.flip_rect(&rect2),
DeviceIntRect::new(Point2D::new(0, 0), Point2D::new(800, 500))
);
assert_eq!(
coordinates.flip_rect(&rect3),
DeviceIntRect::new(Point2D::new(0, 100), Point2D::new(800, 400))
);
// Check rects with different x positions.
let rect1 = DeviceIntRect::new(Point2D::new(0, 0), Point2D::new(700, 400));
let rect2 = DeviceIntRect::new(Point2D::new(100, 100), Point2D::new(800, 600));
let rect3 = DeviceIntRect::new(Point2D::new(300, 200), Point2D::new(600, 500));
assert_eq!(
coordinates.flip_rect(&rect1),
DeviceIntRect::new(Point2D::new(0, 200), Point2D::new(700, 600))
);
assert_eq!(
coordinates.flip_rect(&rect2),
DeviceIntRect::new(Point2D::new(100, 0), Point2D::new(800, 500))
);
assert_eq!(
coordinates.flip_rect(&rect3),
DeviceIntRect::new(Point2D::new(300, 100), Point2D::new(600, 400))
);
}
}

View file

@ -70,6 +70,7 @@ constellation = { path = "../constellation" }
crossbeam-channel = { workspace = true }
devtools = { path = "../devtools" }
devtools_traits = { workspace = true }
dpi = { workspace = true }
embedder_traits = { workspace = true }
env_logger = { workspace = true }
euclid = { workspace = true }

View file

@ -102,8 +102,7 @@ use webgpu::swapchain::WGPUImageMap;
use webrender::{RenderApiSender, ShaderPrecacheFlags, UploadMethod, ONE_TIME_USAGE_HINT};
use webrender_api::{ColorF, DocumentId, FramePublishId};
pub use webrender_traits::rendering_context::{
OffscreenRenderingContext, RenderingContext, SoftwareRenderingContext, SurfmanRenderingContext,
WindowRenderingContext,
OffscreenRenderingContext, RenderingContext, SoftwareRenderingContext, WindowRenderingContext,
};
use webrender_traits::{
CrossProcessCompositorApi, WebrenderExternalImageHandlers, WebrenderExternalImageRegistry,
@ -341,7 +340,7 @@ impl Servo {
let coordinates: compositing::windowing::EmbedderCoordinates = window.get_coordinates();
let device_pixel_ratio = coordinates.hidpi_factor.get();
let viewport_size = coordinates.viewport.size().to_f32() / device_pixel_ratio;
let viewport_size = rendering_context.size2d();
let (mut webrender, webrender_api_sender) = {
let mut debug_flags = webrender::DebugFlags::empty();
@ -408,7 +407,7 @@ impl Servo {
};
let webrender_api = webrender_api_sender.create_api();
let webrender_document = webrender_api.add_document(coordinates.get_viewport().size());
let webrender_document = webrender_api.add_document(viewport_size.to_i32());
// Important that this call is done in a single-threaded fashion, we
// can't defer it after `create_constellation` has started.
@ -472,8 +471,9 @@ impl Servo {
// The division by 1 represents the page's default zoom of 100%,
// and gives us the appropriate CSSPixel type for the viewport.
let scaled_viewport_size = viewport_size.to_f32().to_untyped() / device_pixel_ratio;
let window_size = WindowSizeData {
initial_viewport: viewport_size / Scale::new(1.0),
initial_viewport: scaled_viewport_size / Scale::new(1.0),
device_pixel_ratio: Scale::new(device_pixel_ratio),
};

View file

@ -11,6 +11,7 @@ use base::id::WebViewId;
use compositing::windowing::WebRenderDebugOption;
use compositing::IOCompositor;
use compositing_traits::ConstellationMsg;
use dpi::PhysicalSize;
use embedder_traits::{
Cursor, InputEvent, LoadStatus, MediaSessionActionType, Theme, TouchEventType,
TraversalDirection,
@ -366,11 +367,11 @@ impl WebView {
self.inner().compositor.borrow_mut().on_vsync();
}
pub fn notify_rendering_context_resized(&self) {
pub fn resize(&self, new_size: PhysicalSize<u32>) {
self.inner()
.compositor
.borrow_mut()
.on_rendering_context_resized();
.resize_rendering_context(new_size);
}
pub fn notify_embedder_window_moved(&self) {

View file

@ -10,8 +10,8 @@ use std::num::NonZeroU32;
use std::rc::Rc;
use dpi::PhysicalSize;
use euclid::default::{Rect, Size2D};
use euclid::Point2D;
use euclid::default::{Rect, Size2D as UntypedSize2D};
use euclid::{Point2D, Size2D};
use gleam::gl::{self, Gl};
use glow::NativeFramebuffer;
use image::RgbaImage;
@ -23,6 +23,7 @@ use surfman::{
Adapter, Connection, Context, ContextAttributeFlags, ContextAttributes, Device, GLApi,
NativeContext, NativeWidget, Surface, SurfaceAccess, SurfaceInfo, SurfaceTexture, SurfaceType,
};
use webrender_api::units::{DeviceIntRect, DevicePixel};
/// The `RenderingContext` trait defines a set of methods for managing
/// an OpenGL or GLES rendering context.
@ -40,9 +41,16 @@ pub trait RenderingContext {
/// In a double-buffered [`RenderingContext`] this is expected to read from the back
/// buffer. That means that once Servo renders to the context, this should return those
/// results, even before [`RenderingContext::present`] is called.
fn read_to_image(&self, source_rectangle: Rect<u32>) -> Option<RgbaImage>;
fn read_to_image(&self, source_rectangle: DeviceIntRect) -> Option<RgbaImage>;
/// Get the current size of this [`RenderingContext`].
fn size(&self) -> PhysicalSize<u32>;
/// Get the current size of this [`RenderingContext`] as [`Size2D`].
fn size2d(&self) -> Size2D<u32, DevicePixel> {
let size = self.size();
Size2D::new(size.width, size.height)
}
/// Resizes the rendering surface to the given size.
fn resize(&self, size: Size2D<i32>);
fn resize(&self, size: PhysicalSize<u32>);
/// Presents the rendered frame to the screen. In a double-buffered context, this would
/// swap buffers.
fn present(&self);
@ -54,7 +62,10 @@ pub trait RenderingContext {
fn gl_api(&self) -> Rc<dyn gleam::gl::Gl>;
/// Creates a texture from a given surface and returns the surface texture,
/// the OpenGL texture object, and the size of the surface. Default to `None`.
fn create_texture(&self, _surface: Surface) -> Option<(SurfaceTexture, u32, Size2D<i32>)> {
fn create_texture(
&self,
_surface: Surface,
) -> Option<(SurfaceTexture, u32, UntypedSize2D<i32>)> {
None
}
/// Destroys the texture and returns the surface. Default to `None`.
@ -75,7 +86,7 @@ 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.
pub struct SurfmanRenderingContext {
struct SurfmanRenderingContext {
gl: Rc<dyn Gl>,
device: RefCell<Device>,
context: RefCell<Context>,
@ -148,7 +159,8 @@ impl SurfmanRenderingContext {
SwapChain::create_attached(device, context, SurfaceAccess::GPUOnly)
}
fn resize_surface(&self, size: Size2D<i32>) -> Result<(), Error> {
fn resize_surface(&self, size: PhysicalSize<u32>) -> Result<(), Error> {
let size = Size2D::new(size.width as i32, size.height as i32);
let device = &mut self.device.borrow_mut();
let context = &mut self.context.borrow_mut();
@ -191,12 +203,6 @@ impl SurfmanRenderingContext {
.unwrap_or(None)
.and_then(|info| info.framebuffer_object)
}
}
impl RenderingContext for SurfmanRenderingContext {
fn gl_api(&self) -> Rc<dyn gleam::gl::Gl> {
self.gl.clone()
}
fn prepare_for_rendering(&self) {
let framebuffer_id = self
@ -206,32 +212,23 @@ impl RenderingContext for SurfmanRenderingContext {
.bind_framebuffer(gleam::gl::FRAMEBUFFER, framebuffer_id);
}
fn read_to_image(&self, source_rectangle: Rect<u32>) -> Option<RgbaImage> {
fn read_to_image(&self, source_rectangle: DeviceIntRect) -> Option<RgbaImage> {
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<i32>) {
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<i32>)> {
fn create_texture(
&self,
surface: Surface,
) -> Option<(SurfaceTexture, u32, UntypedSize2D<i32>)> {
let device = &self.device.borrow();
let context = &mut self.context.borrow_mut();
let SurfaceInfo {
@ -268,6 +265,7 @@ impl RenderingContext for SurfmanRenderingContext {
///
/// The results of the render can be accessed via [`RenderingContext::read_to_image`].
pub struct SoftwareRenderingContext {
size: Cell<PhysicalSize<u32>>,
surfman_rendering_info: SurfmanRenderingContext,
swap_chain: SwapChain<Device>,
}
@ -278,13 +276,15 @@ impl SoftwareRenderingContext {
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 })?;
let surfman_size = Size2D::new(size.width as i32, size.height as i32);
let surface =
surfman_rendering_info.create_surface(SurfaceType::Generic { size: surfman_size })?;
surfman_rendering_info.bind_surface(surface)?;
surfman_rendering_info.make_current()?;
let swap_chain = surfman_rendering_info.create_attached_swap_chain()?;
Ok(SoftwareRenderingContext {
size: Cell::new(size),
surfman_rendering_info,
swap_chain,
})
@ -304,13 +304,24 @@ impl RenderingContext for SoftwareRenderingContext {
self.surfman_rendering_info.prepare_for_rendering();
}
fn read_to_image(&self, source_rectangle: Rect<u32>) -> Option<RgbaImage> {
fn read_to_image(&self, source_rectangle: DeviceIntRect) -> Option<RgbaImage> {
self.surfman_rendering_info.read_to_image(source_rectangle)
}
fn resize(&self, size: Size2D<i32>) {
fn size(&self) -> PhysicalSize<u32> {
self.size.get()
}
fn resize(&self, size: PhysicalSize<u32>) {
if self.size.get() == size {
return;
}
self.size.set(size);
let device = &mut self.surfman_rendering_info.device.borrow_mut();
let context = &mut self.surfman_rendering_info.context.borrow_mut();
let size = Size2D::new(size.width as i32, size.height as i32);
let _ = self.swap_chain.resize(device, context, size);
}
@ -331,7 +342,10 @@ impl RenderingContext for SoftwareRenderingContext {
self.surfman_rendering_info.gl.clone()
}
fn create_texture(&self, surface: Surface) -> Option<(SurfaceTexture, u32, Size2D<i32>)> {
fn create_texture(
&self,
surface: Surface,
) -> Option<(SurfaceTexture, u32, UntypedSize2D<i32>)> {
self.surfman_rendering_info.create_texture(surface)
}
@ -352,17 +366,20 @@ impl RenderingContext for SoftwareRenderingContext {
///
/// 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);
pub struct WindowRenderingContext {
size: Cell<PhysicalSize<u32>>,
surfman_context: SurfmanRenderingContext,
}
impl WindowRenderingContext {
pub fn new(
display_handle: DisplayHandle,
window_handle: WindowHandle,
size: &PhysicalSize<u32>,
size: PhysicalSize<u32>,
) -> Result<Self, Error> {
let connection = Connection::from_display_handle(display_handle)?;
let adapter = connection.create_adapter()?;
let surfman_rendering_info = SurfmanRenderingContext::new(&connection, &adapter)?;
let surfman_context = SurfmanRenderingContext::new(&connection, &adapter)?;
let native_widget = connection
.create_native_widget_from_window_handle(
@ -371,22 +388,27 @@ impl WindowRenderingContext {
)
.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()?;
let surface = surfman_context.create_surface(SurfaceType::Widget { native_widget })?;
surfman_context.bind_surface(surface)?;
surfman_context.make_current()?;
Ok(Self(surfman_rendering_info))
Ok(Self {
size: Cell::new(size),
surfman_context,
})
}
pub fn offscreen_context(self: &Rc<Self>, size: Size2D<u32>) -> OffscreenRenderingContext {
pub fn offscreen_context(
self: &Rc<Self>,
size: PhysicalSize<u32>,
) -> 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();
let device = &self.surfman_context.device.borrow();
let context = &self.surfman_context.context.borrow();
device.get_proc_address(context, name)
}
@ -395,8 +417,8 @@ impl WindowRenderingContext {
///
/// 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 device = self.surfman_context.device.borrow_mut();
let mut context = self.surfman_context.context.borrow_mut();
let mut surface = device.unbind_surface_from_context(&mut context)?.unwrap();
device.destroy_surface(&mut context, &mut surface)?;
Ok(())
@ -409,10 +431,10 @@ impl WindowRenderingContext {
pub fn set_window(
&self,
window_handle: WindowHandle,
size: &PhysicalSize<u32>,
size: PhysicalSize<u32>,
) -> Result<(), Error> {
let mut device = self.0.device.borrow_mut();
let mut context = self.0.context.borrow_mut();
let mut device = self.surfman_context.device.borrow_mut();
let mut context = self.surfman_context.context.borrow_mut();
let native_widget = device
.connection()
@ -437,56 +459,67 @@ impl WindowRenderingContext {
}
pub fn surfman_details(&self) -> (RefMut<Device>, RefMut<Context>) {
(self.0.device.borrow_mut(), self.0.context.borrow_mut())
(
self.surfman_context.device.borrow_mut(),
self.surfman_context.context.borrow_mut(),
)
}
}
impl RenderingContext for WindowRenderingContext {
fn prepare_for_rendering(&self) {
self.0.prepare_for_rendering();
self.surfman_context.prepare_for_rendering();
}
fn read_to_image(&self, source_rectangle: Rect<u32>) -> Option<RgbaImage> {
self.0.read_to_image(source_rectangle)
fn read_to_image(&self, source_rectangle: DeviceIntRect) -> Option<RgbaImage> {
self.surfman_context.read_to_image(source_rectangle)
}
fn resize(&self, size: Size2D<i32>) {
if let Err(error) = self.0.resize_surface(size) {
warn!("Error resizing surface: {error:?}");
fn size(&self) -> PhysicalSize<u32> {
self.size.get()
}
fn resize(&self, size: PhysicalSize<u32>) {
match self.surfman_context.resize_surface(size) {
Ok(..) => self.size.set(size),
Err(error) => warn!("Error resizing surface: {error:?}"),
}
}
fn present(&self) {
if let Err(error) = self.0.present_bound_surface() {
if let Err(error) = self.surfman_context.present_bound_surface() {
warn!("Error presenting surface: {error:?}");
}
}
fn make_current(&self) -> Result<(), Error> {
self.0.make_current()
self.surfman_context.make_current()
}
#[allow(unsafe_code)]
fn gl_api(&self) -> Rc<dyn gleam::gl::Gl> {
self.0.gl.clone()
self.surfman_context.gl.clone()
}
fn create_texture(&self, surface: Surface) -> Option<(SurfaceTexture, u32, Size2D<i32>)> {
self.0.create_texture(surface)
fn create_texture(
&self,
surface: Surface,
) -> Option<(SurfaceTexture, u32, UntypedSize2D<i32>)> {
self.surfman_context.create_texture(surface)
}
fn destroy_texture(&self, surface_texture: SurfaceTexture) -> Option<Surface> {
self.0.destroy_texture(surface_texture)
self.surfman_context.destroy_texture(surface_texture)
}
fn connection(&self) -> Option<Connection> {
self.0.connection()
self.surfman_context.connection()
}
}
struct Framebuffer {
gl: Rc<dyn Gl>,
size: Size2D<u32>,
size: PhysicalSize<u32>,
framebuffer_id: gl::GLuint,
renderbuffer_id: gl::GLuint,
texture_id: gl::GLuint,
@ -510,7 +543,7 @@ impl Drop for Framebuffer {
}
impl Framebuffer {
fn new(gl: Rc<dyn Gl>, size: Size2D<u32>) -> Self {
fn new(gl: Rc<dyn Gl>, size: PhysicalSize<u32>) -> Self {
let framebuffer_ids = gl.gen_framebuffers(1);
gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer_ids[0]);
@ -577,14 +610,14 @@ impl Framebuffer {
}
}
fn read_to_image(&self, source_rectangle: Rect<u32>) -> Option<RgbaImage> {
fn read_to_image(&self, source_rectangle: DeviceIntRect) -> Option<RgbaImage> {
Self::read_framebuffer_to_image(&self.gl, self.framebuffer_id, source_rectangle)
}
fn read_framebuffer_to_image(
gl: &Rc<dyn Gl>,
framebuffer_id: u32,
source_rectangle: Rect<u32>,
source_rectangle: DeviceIntRect,
) -> Option<RgbaImage> {
gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer_id);
@ -597,10 +630,10 @@ impl Framebuffer {
gl.bind_vertex_array(0);
let mut pixels = gl.read_pixels(
source_rectangle.origin.x as i32,
source_rectangle.origin.y as i32,
source_rectangle.width() as gl::GLsizei,
source_rectangle.height() as gl::GLsizei,
source_rectangle.min.x,
source_rectangle.min.y,
source_rectangle.width(),
source_rectangle.height(),
gl::RGBA,
gl::UNSIGNED_BYTE,
);
@ -630,7 +663,7 @@ impl Framebuffer {
pub struct OffscreenRenderingContext {
parent_context: Rc<WindowRenderingContext>,
size: Cell<Size2D<u32>>,
size: Cell<PhysicalSize<u32>>,
back_framebuffer: RefCell<Framebuffer>,
front_framebuffer: RefCell<Option<Framebuffer>>,
}
@ -638,7 +671,7 @@ pub struct OffscreenRenderingContext {
type RenderToParentCallback = Box<dyn Fn(&glow::Context, Rect<i32>) + Send + Sync>;
impl OffscreenRenderingContext {
fn new(parent_context: Rc<WindowRenderingContext>, size: Size2D<u32>) -> Self {
fn new(parent_context: Rc<WindowRenderingContext>, size: PhysicalSize<u32>) -> Self {
let next_framebuffer = Framebuffer::new(parent_context.gl_api(), size);
Self {
parent_context,
@ -663,8 +696,9 @@ 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.0.framebuffer();
let parent_context_framebuffer_id = self.parent_context.surfman_context.framebuffer();
let size = self.size.get();
let size = Size2D::new(size.width as i32, size.height as i32);
Some(Box::new(move |gl, target_rect| {
Self::render_framebuffer_to_parent_context(
gl,
@ -718,11 +752,15 @@ impl OffscreenRenderingContext {
}
impl RenderingContext for OffscreenRenderingContext {
fn resize(&self, size: Size2D<i32>) {
fn size(&self) -> PhysicalSize<u32> {
self.size.get()
}
fn resize(&self, size: PhysicalSize<u32>) {
// We do not resize any buffers right now. The current buffers might be too big or too
// small, but we only want to ensure (later) that next buffer that we draw to is the
// correct size.
self.size.set(size.to_u32());
self.size.set(size);
}
fn prepare_for_rendering(&self) {
@ -756,7 +794,10 @@ impl RenderingContext for OffscreenRenderingContext {
self.parent_context.gl_api()
}
fn create_texture(&self, surface: Surface) -> Option<(SurfaceTexture, u32, Size2D<i32>)> {
fn create_texture(
&self,
surface: Surface,
) -> Option<(SurfaceTexture, u32, UntypedSize2D<i32>)> {
self.parent_context.create_texture(surface)
}
@ -768,7 +809,7 @@ impl RenderingContext for OffscreenRenderingContext {
self.parent_context.connection()
}
fn read_to_image(&self, source_rectangle: Rect<u32>) -> Option<RgbaImage> {
fn read_to_image(&self, source_rectangle: DeviceIntRect) -> Option<RgbaImage> {
self.back_framebuffer
.borrow()
.read_to_image(source_rectangle)
@ -777,7 +818,8 @@ impl RenderingContext for OffscreenRenderingContext {
#[cfg(test)]
mod test {
use euclid::{Point2D, Rect, Size2D};
use dpi::PhysicalSize;
use euclid::{Box2D, Point2D, Size2D};
use gleam::gl;
use image::Rgba;
use surfman::{Connection, ContextAttributeFlags, ContextAttributes, Error, GLApi, GLVersion};
@ -807,15 +849,16 @@ mod test {
{
const SIZE: u32 = 16;
let framebuffer = Framebuffer::new(gl, Size2D::new(SIZE, SIZE));
let framebuffer = Framebuffer::new(gl, PhysicalSize::new(SIZE, SIZE));
framebuffer.bind();
framebuffer
.gl
.clear_color(12.0 / 255.0, 34.0 / 255.0, 56.0 / 255.0, 78.0 / 255.0);
framebuffer.gl.clear(gl::COLOR_BUFFER_BIT);
let rect = Box2D::from_origin_and_size(Point2D::zero(), Size2D::new(SIZE, SIZE));
let img = framebuffer
.read_to_image(Rect::new(Point2D::zero(), Size2D::new(SIZE, SIZE)))
.read_to_image(rect.to_i32())
.expect("Should have been able to read back image.");
assert_eq!(img.width(), SIZE);
assert_eq!(img.height(), SIZE);

View file

@ -7,14 +7,14 @@ use std::collections::HashMap;
use std::path::PathBuf;
use std::rc::Rc;
use euclid::Vector2D;
use euclid::{Point2D, Vector2D};
use image::{DynamicImage, ImageFormat};
use keyboard_types::{Key, KeyboardEvent, Modifiers, ShortcutMatcher};
use log::{error, info};
use servo::base::id::WebViewId;
use servo::config::pref;
use servo::ipc_channel::ipc::IpcSender;
use servo::webrender_api::units::{DeviceIntPoint, DeviceIntSize};
use servo::webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize};
use servo::webrender_api::ScrollLocation;
use servo::{
AllowOrDenyRequest, AuthenticationRequest, FilterPattern, GamepadHapticEffectType, LoadStatus,
@ -130,13 +130,8 @@ impl RunningAppState {
};
let inner = self.inner();
let viewport_rect = inner
.window
.get_coordinates()
.viewport
.to_rect()
.to_untyped()
.to_u32();
let size = inner.window.rendering_context().size2d().to_i32();
let viewport_rect = DeviceIntRect::from_origin_and_size(Point2D::origin(), size);
let Some(image) = inner
.window
.rendering_context()
@ -467,17 +462,8 @@ impl WebViewDelegate for RunningAppState {
}
fn notify_ready_to_show(&self, webview: servo::WebView) {
let rect = self
.inner()
.window
.get_coordinates()
.get_viewport()
.to_f32();
webview.focus();
webview.move_resize(rect);
webview.raise_to_top(true);
webview.notify_rendering_context_resized();
}
fn notify_closed(&self, webview: servo::WebView) {

View file

@ -121,7 +121,7 @@ impl Window {
.window_handle()
.expect("could not get window handle from window");
let window_rendering_context = Rc::new(
WindowRenderingContext::new(display_handle, window_handle, &inner_size)
WindowRenderingContext::new(display_handle, window_handle, inner_size)
.expect("Could not create RenderingContext for Window"),
);
@ -135,9 +135,7 @@ impl Window {
// Make sure the gl context is made current.
window_rendering_context.make_current().unwrap();
let rendering_context_size = Size2D::new(inner_size.width, inner_size.height);
let rendering_context =
Rc::new(window_rendering_context.offscreen_context(rendering_context_size));
let rendering_context = Rc::new(window_rendering_context.offscreen_context(inner_size));
debug!("Created window {:?}", winit_window.id());
Window {
@ -614,11 +612,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);
self.window_rendering_context
.resize(rendering_context_size.to_i32());
self.window_rendering_context.resize(new_size);
self.inner_size.set(new_size);
webview.notify_rendering_context_resized();
}
},
WindowEvent::ThemeChanged(theme) => {
@ -736,17 +731,9 @@ impl WindowMethods for Window {
let window_scale: Scale<f64, DeviceIndependentPixel, DevicePixel> =
Scale::new(self.winit_window.scale_factor());
let window_rect = (window_rect.to_f64() / window_scale).to_i32();
let viewport_origin = DeviceIntPoint::zero(); // bottom left
let mut viewport_size = winit_size_to_euclid_size(self.winit_window.inner_size()).to_f32();
viewport_size.height -= (self.toolbar_height() * self.hidpi_factor()).0;
let viewport = DeviceIntRect::from_origin_and_size(viewport_origin, viewport_size.to_i32());
let screen_size = self.screen_size.to_i32();
EmbedderCoordinates {
viewport,
framebuffer: viewport.size(),
window_rect,
screen_size,
// FIXME: Winit doesn't have API for available size. Fallback to screen size

View file

@ -86,7 +86,7 @@ impl WindowPortsMethods for Window {
// Because we are managing the rendering surface ourselves, there will be no other
// notification (such as from the display manager) that it has changed size, so we
// must notify the compositor here.
webview.notify_rendering_context_resized();
webview.resize(PhysicalSize::new(size.width as u32, size.height as u32));
Some(new_size)
}
@ -149,10 +149,7 @@ impl WindowPortsMethods for Window {
impl WindowMethods for Window {
fn get_coordinates(&self) -> EmbedderCoordinates {
let inner_size = self.inner_size.get();
EmbedderCoordinates {
viewport: Box2D::from_origin_and_size(Point2D::zero(), inner_size),
framebuffer: inner_size,
window_rect: self.window_rect,
screen_size: self.screen_size,
available_screen_size: self.screen_size,

View file

@ -7,6 +7,7 @@ use std::rc::Rc;
use std::sync::Arc;
use std::time::Instant;
use dpi::PhysicalSize;
use egui::text::{CCursor, CCursorRange};
use egui::text_edit::TextEditState;
use egui::{
@ -379,13 +380,11 @@ impl Minibrowser {
// If the top parts of the GUI changed size, then update the size of the WebView and also
// the size of its RenderingContext.
let available_size = ui.available_size();
let rect = Box2D::from_origin_and_size(
Point2D::origin(),
Size2D::new(available_size.x, available_size.y),
) * scale;
let size = Size2D::new(available_size.x, available_size.y) * scale;
let rect = Box2D::from_origin_and_size(Point2D::origin(), size);
if rect != webview.rect() {
webview.move_resize(rect);
rendering_context.resize(rect.size().to_i32().to_untyped());
webview.resize(PhysicalSize::new(size.width as u32, size.height as u32))
}
let min = ui.cursor().min;

View file

@ -713,13 +713,7 @@ fn jni_coords_to_rust_coords<'local>(
let height = get_non_null_field(env, obj, "height", "I")?
.i()
.map_err(|_| "height not an int")? as i32;
let fb_width = get_non_null_field(env, obj, "fb_width", "I")?
.i()
.map_err(|_| "fb_width not an int")? as i32;
let fb_height = get_non_null_field(env, obj, "fb_height", "I")?
.i()
.map_err(|_| "fb_height not an int")? as i32;
Ok(Coordinates::new(x, y, width, height, fb_width, fb_height))
Ok(Coordinates::new(x, y, width, height))
}
fn get_field<'local>(

View file

@ -6,6 +6,7 @@ use std::cell::RefCell;
use std::mem;
use std::rc::Rc;
use dpi::PhysicalSize;
use raw_window_handle::{DisplayHandle, RawDisplayHandle, RawWindowHandle, WindowHandle};
pub use servo::webrender_api::units::DeviceIntRect;
/// The EventLoopWaker::wake function will be called from any thread.
@ -68,11 +69,13 @@ pub fn init(
WindowHandle::borrow_raw(init_opts.window_handle),
)
};
let size = init_opts.coordinates.viewport.size;
let rendering_context = Rc::new(
WindowRenderingContext::new(
display_handle,
window_handle,
&init_opts.coordinates.framebuffer_size(),
PhysicalSize::new(size.width as u32, size.height as u32),
)
.expect("Could not create RenderingContext"),
);

View file

@ -16,7 +16,7 @@ use servo::compositing::windowing::{
};
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::units::{DeviceIntRect, DeviceIntSize, DevicePixel};
use servo::webrender_api::ScrollLocation;
use servo::{
AllowOrDenyRequest, ContextMenuResult, EmbedderProxy, EventLoopWaker, ImeEvent, InputEvent,
@ -34,30 +34,14 @@ use crate::prefs::ServoShellPreferences;
#[derive(Clone, Debug)]
pub struct Coordinates {
pub viewport: Rect<i32, DevicePixel>,
pub framebuffer: Size2D<i32, DevicePixel>,
}
impl Coordinates {
pub fn new(
x: i32,
y: i32,
width: i32,
height: i32,
fb_width: i32,
fb_height: i32,
) -> Coordinates {
pub fn new(x: i32, y: i32, width: i32, height: i32) -> Coordinates {
Coordinates {
viewport: Rect::new(Point2D::new(x, y), Size2D::new(width, height)),
framebuffer: Size2D::new(fb_width, fb_height),
}
}
pub(crate) fn framebuffer_size(&self) -> PhysicalSize<u32> {
PhysicalSize::new(
self.framebuffer.width as u32,
self.framebuffer.height as u32,
)
}
}
pub(super) struct ServoWindowCallbacks {
@ -426,14 +410,12 @@ impl RunningAppState {
/// Let Servo know that the window has been resized.
pub fn resize(&self, coordinates: Coordinates) {
info!("resize to {:?}", coordinates);
let size = coordinates.viewport.size;
self.rendering_context
.resize(Size2D::new(size.width, size.height));
info!("resize to {:?}", coordinates,);
self.active_webview().resize(PhysicalSize::new(
coordinates.viewport.width() as u32,
coordinates.viewport.height() as u32,
));
*self.callbacks.coordinates.borrow_mut() = coordinates;
self.active_webview().notify_rendering_context_resized();
self.active_webview()
.move_resize(DeviceRect::from_size(size.to_f32()));
self.perform_updates();
}
@ -633,9 +615,10 @@ impl RunningAppState {
pub fn resume_compositor(&self, window_handle: RawWindowHandle, coords: Coordinates) {
let window_handle = unsafe { WindowHandle::borrow_raw(window_handle) };
let size = coords.viewport.size.to_u32();
if let Err(e) = self
.rendering_context
.set_window(window_handle, &coords.framebuffer_size())
.set_window(window_handle, PhysicalSize::new(size.width, size.height))
{
warn!("Binding native surface to context failed ({:?})", e);
}
@ -723,8 +706,6 @@ impl WindowMethods for ServoWindowCallbacks {
let coords = self.coordinates.borrow();
let screen_size = (coords.viewport.size.to_f32() / self.hidpi_factor).to_i32();
EmbedderCoordinates {
viewport: coords.viewport.to_box2d(),
framebuffer: coords.framebuffer,
window_rect: Box2D::from_origin_and_size(Point2D::zero(), screen_size),
screen_size,
available_screen_size: screen_size,

View file

@ -184,9 +184,7 @@ impl ServoAction {
servo.notify_vsync();
servo.present_if_needed();
},
Resize { width, height } => {
servo.resize(Coordinates::new(0, 0, *width, *height, *width, *height))
},
Resize { width, height } => servo.resize(Coordinates::new(0, 0, *width, *height)),
};
}
}

View file

@ -7,6 +7,7 @@ use std::path::PathBuf;
use std::ptr::NonNull;
use std::rc::Rc;
use dpi::PhysicalSize;
use log::{debug, info};
use raw_window_handle::{
DisplayHandle, OhosDisplayHandle, OhosNdkWindowHandle, RawDisplayHandle, RawWindowHandle,
@ -66,14 +67,7 @@ pub fn init(
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,
);
let coordinates = Coordinates::new(0, 0, window_size.width, window_size.height);
let display_handle = RawDisplayHandle::Ohos(OhosDisplayHandle::new());
let display_handle = unsafe { DisplayHandle::borrow_raw(display_handle) };
@ -86,7 +80,7 @@ pub fn init(
WindowRenderingContext::new(
display_handle,
window_handle,
&coordinates.framebuffer_size(),
PhysicalSize::new(window_size.width as u32, window_size.height as u32),
)
.expect("Could not create RenderingContext"),
);

View file

@ -85,8 +85,6 @@ public class JNIServo {
public int y = 0;
public int width = 0;
public int height = 0;
public int fb_width = 0;
public int fb_height = 0;
}
public interface Callbacks {

View file

@ -372,8 +372,6 @@ public class ServoView extends SurfaceView
ServoCoordinates coords = new ServoCoordinates();
coords.width = mServoView.getWidth();
coords.height = mServoView.getHeight();
coords.fb_width = mServoView.getWidth();
coords.fb_height = mServoView.getHeight();
Surface surface = holder.getSurface();
ServoOptions options = new ServoOptions();
@ -401,9 +399,6 @@ public class ServoView extends SurfaceView
ServoCoordinates coords = new ServoCoordinates();
coords.width = width;
coords.height = height;
coords.fb_width = width;
coords.fb_height = height;
mServoView.mServo.resize(coords);
}