1#![deny(unsafe_code)]
6
7use std::cell::{Cell, RefCell, RefMut};
8use std::num::NonZeroU32;
9use std::rc::Rc;
10use std::sync::Arc;
11
12use dpi::PhysicalSize;
13use embedder_traits::RefreshDriver;
14use euclid::default::{Rect, Size2D as UntypedSize2D};
15use euclid::{Point2D, Size2D};
16use gleam::gl::{self, Gl};
17use glow::{HasContext, NativeFramebuffer};
18use image::RgbaImage;
19use log::{debug, trace, warn};
20use raw_window_handle::{DisplayHandle, WindowHandle};
21pub use surfman::Error;
22use surfman::chains::{PreserveBuffer, SwapChain};
23use surfman::{
24 Adapter, Connection, Context, ContextAttributeFlags, ContextAttributes, Device, GLApi,
25 GLVersion, NativeWidget, Surface, SurfaceAccess, SurfaceInfo, SurfaceTexture, SurfaceType,
26};
27use webrender_api::units::{DeviceIntRect, DevicePixel};
28
29pub trait RenderingContext {
35 fn prepare_for_rendering(&self) {}
38 fn read_to_image(&self, source_rectangle: DeviceIntRect) -> Option<RgbaImage>;
46 fn size(&self) -> PhysicalSize<u32>;
48 fn size2d(&self) -> Size2D<u32, DevicePixel> {
50 let size = self.size();
51 Size2D::new(size.width, size.height)
52 }
53 fn resize(&self, size: PhysicalSize<u32>);
55 fn present(&self);
58 fn make_current(&self) -> Result<(), Error>;
62 fn gleam_gl_api(&self) -> Rc<dyn gleam::gl::Gl>;
64 fn glow_gl_api(&self) -> Arc<glow::Context>;
66 fn create_texture(
69 &self,
70 _surface: Surface,
71 ) -> Option<(SurfaceTexture, u32, UntypedSize2D<i32>)> {
72 None
73 }
74 fn destroy_texture(&self, _surface_texture: SurfaceTexture) -> Option<Surface> {
76 None
77 }
78 fn connection(&self) -> Option<Connection> {
80 None
81 }
82 fn refresh_driver(&self) -> Option<Rc<dyn RefreshDriver>> {
85 None
86 }
87}
88
89struct SurfmanRenderingContext {
98 gleam_gl: Rc<dyn Gl>,
99 glow_gl: Arc<glow::Context>,
100 device: RefCell<Device>,
101 context: RefCell<Context>,
102 refresh_driver: Option<Rc<dyn RefreshDriver>>,
103}
104
105impl Drop for SurfmanRenderingContext {
106 fn drop(&mut self) {
107 let device = &mut self.device.borrow_mut();
108 let context = &mut self.context.borrow_mut();
109 let _ = device.destroy_context(context);
110 }
111}
112
113impl SurfmanRenderingContext {
114 fn new(
115 connection: &Connection,
116 adapter: &Adapter,
117 refresh_driver: Option<Rc<dyn RefreshDriver>>,
118 ) -> Result<Self, Error> {
119 let device = connection.create_device(adapter)?;
120
121 let flags = ContextAttributeFlags::ALPHA |
122 ContextAttributeFlags::DEPTH |
123 ContextAttributeFlags::STENCIL;
124 let gl_api = connection.gl_api();
125 let version = match &gl_api {
126 GLApi::GLES => surfman::GLVersion { major: 3, minor: 0 },
127 GLApi::GL => surfman::GLVersion { major: 3, minor: 2 },
128 };
129 let context_descriptor =
130 device.create_context_descriptor(&ContextAttributes { flags, version })?;
131
132 let context = device
133 .create_context(&context_descriptor, None)
134 .inspect_err(|_| {
135 print_diagnostics_information_on_context_creation_failure(&device, gl_api, version)
136 })?;
137
138 #[expect(unsafe_code)]
139 let gleam_gl = {
140 match gl_api {
141 GLApi::GL => unsafe {
142 gl::GlFns::load_with(|func_name| device.get_proc_address(&context, func_name))
143 },
144 GLApi::GLES => unsafe {
145 gl::GlesFns::load_with(|func_name| device.get_proc_address(&context, func_name))
146 },
147 }
148 };
149
150 #[expect(unsafe_code)]
151 let glow_gl = unsafe {
152 glow::Context::from_loader_function(|function_name| {
153 device.get_proc_address(&context, function_name)
154 })
155 };
156
157 Ok(SurfmanRenderingContext {
158 gleam_gl,
159 glow_gl: Arc::new(glow_gl),
160 device: RefCell::new(device),
161 context: RefCell::new(context),
162 refresh_driver,
163 })
164 }
165
166 fn create_surface(&self, surface_type: SurfaceType<NativeWidget>) -> Result<Surface, Error> {
167 let device = &mut self.device.borrow_mut();
168 let context = &self.context.borrow();
169 device.create_surface(context, SurfaceAccess::GPUOnly, surface_type)
170 }
171
172 fn bind_surface(&self, surface: Surface) -> Result<(), Error> {
173 let device = &self.device.borrow();
174 let context = &mut self.context.borrow_mut();
175 device
176 .bind_surface_to_context(context, surface)
177 .map_err(|(err, mut surface)| {
178 let _ = device.destroy_surface(context, &mut surface);
179 err
180 })?;
181 Ok(())
182 }
183
184 fn create_attached_swap_chain(&self) -> Result<SwapChain<Device>, Error> {
185 let device = &mut self.device.borrow_mut();
186 let context = &mut self.context.borrow_mut();
187 SwapChain::create_attached(device, context, SurfaceAccess::GPUOnly)
188 }
189
190 fn resize_bound_surface(&self, size: PhysicalSize<u32>) -> Result<(), Error> {
191 if size.width == 0 || size.height == 0 {
192 log::error!("Unable to resize to size under 1x1 ({size:?} provided)");
193 return Err(Error::Failed);
194 }
195
196 let size = Size2D::new(size.width as i32, size.height as i32);
197 self.device
198 .borrow()
199 .resize_bound_surface(&mut self.context.borrow_mut(), size)
200 }
201
202 fn present_bound_surface(&self) -> Result<(), Error> {
203 self.device
204 .borrow()
205 .present_bound_surface(&mut self.context.borrow_mut())
206 }
207
208 fn framebuffer(&self) -> Option<NativeFramebuffer> {
209 let device = &self.device.borrow();
210 let context = &self.context.borrow();
211 device
212 .context_surface_info(context)
213 .unwrap_or(None)
214 .and_then(|info| info.framebuffer_object)
215 }
216
217 fn prepare_for_rendering(&self) {
218 let framebuffer_id = self
219 .framebuffer()
220 .map_or(0, |framebuffer| framebuffer.0.into());
221 self.gleam_gl
222 .bind_framebuffer(gleam::gl::FRAMEBUFFER, framebuffer_id);
223 }
224
225 fn read_to_image(&self, source_rectangle: DeviceIntRect) -> Option<RgbaImage> {
226 let framebuffer_id = self
227 .framebuffer()
228 .map_or(0, |framebuffer| framebuffer.0.into());
229 Framebuffer::read_framebuffer_to_image(&self.gleam_gl, framebuffer_id, source_rectangle)
230 }
231
232 fn make_current(&self) -> Result<(), Error> {
233 let device = &self.device.borrow();
234 let context = &mut self.context.borrow();
235 device.make_context_current(context)
236 }
237
238 fn create_texture(
239 &self,
240 surface: Surface,
241 ) -> Option<(SurfaceTexture, u32, UntypedSize2D<i32>)> {
242 let device = &self.device.borrow();
243 let context = &mut self.context.borrow_mut();
244 let SurfaceInfo {
245 id: front_buffer_id,
246 size,
247 ..
248 } = device.surface_info(&surface);
249 debug!("... getting texture for surface {:?}", front_buffer_id);
250 let surface_texture = device.create_surface_texture(context, surface).unwrap();
251 let gl_texture = device
252 .surface_texture_object(&surface_texture)
253 .map(|tex| tex.0.get())
254 .unwrap_or(0);
255 Some((surface_texture, gl_texture, size))
256 }
257
258 fn destroy_texture(&self, surface_texture: SurfaceTexture) -> Option<Surface> {
259 let device = &self.device.borrow();
260 let context = &mut self.context.borrow_mut();
261 device
262 .destroy_surface_texture(context, surface_texture)
263 .map_err(|(error, _)| error)
264 .ok()
265 }
266
267 fn connection(&self) -> Option<Connection> {
268 Some(self.device.borrow().connection())
269 }
270
271 fn refresh_driver(&self) -> Option<Rc<dyn RefreshDriver>> {
272 self.refresh_driver.clone()
273 }
274}
275
276pub struct SoftwareRenderingContext {
282 size: Cell<PhysicalSize<u32>>,
283 surfman_rendering_info: SurfmanRenderingContext,
284 swap_chain: SwapChain<Device>,
285}
286
287impl SoftwareRenderingContext {
288 pub fn new(size: PhysicalSize<u32>) -> Result<Self, Error> {
289 if size.width == 0 || size.height == 0 {
290 log::error!(
291 "Unable to create SoftwareRenderingContext with size under 1x1 ({size:?} provided)"
292 );
293 return Err(Error::Failed);
294 }
295
296 let connection = Connection::new()?;
297 let adapter = connection.create_software_adapter()?;
298 let surfman_rendering_info = SurfmanRenderingContext::new(&connection, &adapter, None)?;
299
300 let surfman_size = Size2D::new(size.width as i32, size.height as i32);
301 let surface =
302 surfman_rendering_info.create_surface(SurfaceType::Generic { size: surfman_size })?;
303 surfman_rendering_info.bind_surface(surface)?;
304 surfman_rendering_info.make_current()?;
305
306 let swap_chain = surfman_rendering_info.create_attached_swap_chain()?;
307 Ok(SoftwareRenderingContext {
308 size: Cell::new(size),
309 surfman_rendering_info,
310 swap_chain,
311 })
312 }
313}
314
315impl Drop for SoftwareRenderingContext {
316 fn drop(&mut self) {
317 let device = &mut self.surfman_rendering_info.device.borrow_mut();
318 let context = &mut self.surfman_rendering_info.context.borrow_mut();
319 let _ = self.swap_chain.destroy(device, context);
320 }
321}
322
323impl RenderingContext for SoftwareRenderingContext {
324 fn prepare_for_rendering(&self) {
325 self.surfman_rendering_info.prepare_for_rendering();
326 }
327
328 fn read_to_image(&self, source_rectangle: DeviceIntRect) -> Option<RgbaImage> {
329 self.surfman_rendering_info.read_to_image(source_rectangle)
330 }
331
332 fn size(&self) -> PhysicalSize<u32> {
333 self.size.get()
334 }
335
336 fn resize(&self, size: PhysicalSize<u32>) {
337 assert!(
338 size.width > 0 && size.height > 0,
339 "Dimensions must be at least 1x1, got {size:?}",
340 );
341
342 if self.size.get() == size {
343 return;
344 }
345
346 self.size.set(size);
347
348 let device = &mut self.surfman_rendering_info.device.borrow_mut();
349 let context = &mut self.surfman_rendering_info.context.borrow_mut();
350 let size = Size2D::new(size.width as i32, size.height as i32);
351 let _ = self.swap_chain.resize(device, context, size);
352 }
353
354 #[servo_tracing::instrument(skip_all, name = "SoftwareRenderingContext::present")]
355 fn present(&self) {
356 let device = &mut self.surfman_rendering_info.device.borrow_mut();
357 let context = &mut self.surfman_rendering_info.context.borrow_mut();
358 let _ = self
359 .swap_chain
360 .swap_buffers(device, context, PreserveBuffer::No);
361 }
362
363 fn make_current(&self) -> Result<(), Error> {
364 self.surfman_rendering_info.make_current()
365 }
366
367 fn gleam_gl_api(&self) -> Rc<dyn gleam::gl::Gl> {
368 self.surfman_rendering_info.gleam_gl.clone()
369 }
370
371 fn glow_gl_api(&self) -> Arc<glow::Context> {
372 self.surfman_rendering_info.glow_gl.clone()
373 }
374
375 fn create_texture(
376 &self,
377 surface: Surface,
378 ) -> Option<(SurfaceTexture, u32, UntypedSize2D<i32>)> {
379 self.surfman_rendering_info.create_texture(surface)
380 }
381
382 fn destroy_texture(&self, surface_texture: SurfaceTexture) -> Option<Surface> {
383 self.surfman_rendering_info.destroy_texture(surface_texture)
384 }
385
386 fn connection(&self) -> Option<Connection> {
387 self.surfman_rendering_info.connection()
388 }
389}
390
391pub struct WindowRenderingContext {
400 size: Cell<PhysicalSize<u32>>,
402 surfman_context: SurfmanRenderingContext,
403}
404
405impl WindowRenderingContext {
406 pub fn new(
407 display_handle: DisplayHandle,
408 window_handle: WindowHandle,
409 size: PhysicalSize<u32>,
410 ) -> Result<Self, Error> {
411 Self::new_with_optional_refresh_driver(display_handle, window_handle, size, None)
412 }
413
414 pub fn new_with_refresh_driver(
415 display_handle: DisplayHandle,
416 window_handle: WindowHandle,
417 size: PhysicalSize<u32>,
418 refresh_driver: Rc<dyn RefreshDriver>,
419 ) -> Result<Self, Error> {
420 Self::new_with_optional_refresh_driver(
421 display_handle,
422 window_handle,
423 size,
424 Some(refresh_driver),
425 )
426 }
427
428 fn new_with_optional_refresh_driver(
429 display_handle: DisplayHandle,
430 window_handle: WindowHandle,
431 size: PhysicalSize<u32>,
432 refresh_driver: Option<Rc<dyn RefreshDriver>>,
433 ) -> Result<Self, Error> {
434 if size.width == 0 || size.height == 0 {
435 log::error!(
436 "Unable to create WindowRenderingContext with size under 1x1 ({size:?} provided)"
437 );
438 return Err(Error::Failed);
439 }
440
441 let connection = Connection::from_display_handle(display_handle)?;
442 let adapter = connection.create_adapter()?;
443 let surfman_context = SurfmanRenderingContext::new(&connection, &adapter, refresh_driver)?;
444
445 let native_widget = connection
446 .create_native_widget_from_window_handle(
447 window_handle,
448 Size2D::new(size.width as i32, size.height as i32),
449 )
450 .expect("Failed to create native widget");
451
452 let surface = surfman_context.create_surface(SurfaceType::Widget { native_widget })?;
453 surfman_context.bind_surface(surface)?;
454 surfman_context.make_current()?;
455
456 Ok(Self {
457 size: Cell::new(size),
458 surfman_context,
459 })
460 }
461
462 pub fn offscreen_context(
463 self: &Rc<Self>,
464 size: PhysicalSize<u32>,
465 ) -> OffscreenRenderingContext {
466 OffscreenRenderingContext::new(self.clone(), size)
467 }
468
469 pub fn take_window(&self) -> Result<(), Error> {
474 let device = self.surfman_context.device.borrow_mut();
475 let mut context = self.surfman_context.context.borrow_mut();
476 let mut surface = device.unbind_surface_from_context(&mut context)?.unwrap();
477 device.destroy_surface(&mut context, &mut surface)?;
478 Ok(())
479 }
480
481 pub fn set_window(
486 &self,
487 window_handle: WindowHandle,
488 size: PhysicalSize<u32>,
489 ) -> Result<(), Error> {
490 let device = self.surfman_context.device.borrow_mut();
491 let mut context = self.surfman_context.context.borrow_mut();
492
493 let native_widget = device
494 .connection()
495 .create_native_widget_from_window_handle(
496 window_handle,
497 Size2D::new(size.width as i32, size.height as i32),
498 )
499 .expect("Failed to create native widget");
500
501 let surface_access = SurfaceAccess::GPUOnly;
502 let surface_type = SurfaceType::Widget { native_widget };
503 let surface = device.create_surface(&context, surface_access, surface_type)?;
504
505 device
506 .bind_surface_to_context(&mut context, surface)
507 .map_err(|(err, mut surface)| {
508 let _ = device.destroy_surface(&mut context, &mut surface);
509 err
510 })?;
511 device.make_context_current(&context)?;
512 Ok(())
513 }
514
515 pub fn surfman_details(&self) -> (RefMut<'_, Device>, RefMut<'_, Context>) {
516 (
517 self.surfman_context.device.borrow_mut(),
518 self.surfman_context.context.borrow_mut(),
519 )
520 }
521}
522
523impl RenderingContext for WindowRenderingContext {
524 fn prepare_for_rendering(&self) {
525 self.surfman_context.prepare_for_rendering();
526 }
527
528 fn read_to_image(&self, source_rectangle: DeviceIntRect) -> Option<RgbaImage> {
529 self.surfman_context.read_to_image(source_rectangle)
530 }
531
532 fn size(&self) -> PhysicalSize<u32> {
533 self.size.get()
534 }
535
536 fn resize(&self, size: PhysicalSize<u32>) {
537 match self.surfman_context.resize_bound_surface(size) {
538 Ok(..) => self.size.set(size),
539 Err(error) => warn!("Error resizing surface: {error:?}"),
540 }
541 }
542
543 #[servo_tracing::instrument(skip_all, name = "WindowRenderingContext::present")]
544 fn present(&self) {
545 if let Err(error) = self.surfman_context.present_bound_surface() {
546 warn!("Error presenting surface: {error:?}");
547 }
548 }
549
550 fn make_current(&self) -> Result<(), Error> {
551 self.surfman_context.make_current()
552 }
553
554 fn gleam_gl_api(&self) -> Rc<dyn gleam::gl::Gl> {
555 self.surfman_context.gleam_gl.clone()
556 }
557
558 fn glow_gl_api(&self) -> Arc<glow::Context> {
559 self.surfman_context.glow_gl.clone()
560 }
561
562 fn create_texture(
563 &self,
564 surface: Surface,
565 ) -> Option<(SurfaceTexture, u32, UntypedSize2D<i32>)> {
566 self.surfman_context.create_texture(surface)
567 }
568
569 fn destroy_texture(&self, surface_texture: SurfaceTexture) -> Option<Surface> {
570 self.surfman_context.destroy_texture(surface_texture)
571 }
572
573 fn connection(&self) -> Option<Connection> {
574 self.surfman_context.connection()
575 }
576
577 fn refresh_driver(&self) -> Option<Rc<dyn RefreshDriver>> {
578 self.surfman_context.refresh_driver()
579 }
580}
581
582struct Framebuffer {
583 gl: Rc<dyn Gl>,
584 framebuffer_id: gl::GLuint,
585 renderbuffer_id: gl::GLuint,
586 texture_id: gl::GLuint,
587}
588
589impl Framebuffer {
590 fn bind(&self) {
591 trace!("Binding FBO {}", self.framebuffer_id);
592 self.gl
593 .bind_framebuffer(gl::FRAMEBUFFER, self.framebuffer_id)
594 }
595}
596
597impl Drop for Framebuffer {
598 fn drop(&mut self) {
599 self.gl.bind_framebuffer(gl::FRAMEBUFFER, 0);
600 self.gl.delete_textures(&[self.texture_id]);
601 self.gl.delete_renderbuffers(&[self.renderbuffer_id]);
602 self.gl.delete_framebuffers(&[self.framebuffer_id]);
603 }
604}
605
606impl Framebuffer {
607 fn new(gl: Rc<dyn Gl>, size: PhysicalSize<u32>) -> Self {
608 let framebuffer_ids = gl.gen_framebuffers(1);
609 gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer_ids[0]);
610
611 let texture_ids = gl.gen_textures(1);
612 gl.bind_texture(gl::TEXTURE_2D, texture_ids[0]);
613 gl.tex_image_2d(
614 gl::TEXTURE_2D,
615 0,
616 gl::RGBA as gl::GLint,
617 size.width as gl::GLsizei,
618 size.height as gl::GLsizei,
619 0,
620 gl::RGBA,
621 gl::UNSIGNED_BYTE,
622 None,
623 );
624 gl.tex_parameter_i(
625 gl::TEXTURE_2D,
626 gl::TEXTURE_MAG_FILTER,
627 gl::NEAREST as gl::GLint,
628 );
629 gl.tex_parameter_i(
630 gl::TEXTURE_2D,
631 gl::TEXTURE_MIN_FILTER,
632 gl::NEAREST as gl::GLint,
633 );
634
635 gl.framebuffer_texture_2d(
636 gl::FRAMEBUFFER,
637 gl::COLOR_ATTACHMENT0,
638 gl::TEXTURE_2D,
639 texture_ids[0],
640 0,
641 );
642
643 gl.bind_texture(gl::TEXTURE_2D, 0);
644
645 let renderbuffer_ids = gl.gen_renderbuffers(1);
646 let depth_rb = renderbuffer_ids[0];
647 gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb);
648 gl.renderbuffer_storage(
649 gl::RENDERBUFFER,
650 gl::DEPTH_COMPONENT24,
651 size.width as gl::GLsizei,
652 size.height as gl::GLsizei,
653 );
654 gl.framebuffer_renderbuffer(
655 gl::FRAMEBUFFER,
656 gl::DEPTH_ATTACHMENT,
657 gl::RENDERBUFFER,
658 depth_rb,
659 );
660
661 Self {
662 gl,
663 framebuffer_id: *framebuffer_ids
664 .first()
665 .expect("Guaranteed by GL operations"),
666 renderbuffer_id: *renderbuffer_ids
667 .first()
668 .expect("Guaranteed by GL operations"),
669 texture_id: *texture_ids.first().expect("Guaranteed by GL operations"),
670 }
671 }
672
673 fn read_to_image(&self, source_rectangle: DeviceIntRect) -> Option<RgbaImage> {
674 Self::read_framebuffer_to_image(&self.gl, self.framebuffer_id, source_rectangle)
675 }
676
677 fn read_framebuffer_to_image(
678 gl: &Rc<dyn Gl>,
679 framebuffer_id: u32,
680 source_rectangle: DeviceIntRect,
681 ) -> Option<RgbaImage> {
682 gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer_id);
683
684 gl.bind_vertex_array(0);
691
692 let mut pixels = gl.read_pixels(
693 source_rectangle.min.x,
694 source_rectangle.min.y,
695 source_rectangle.width(),
696 source_rectangle.height(),
697 gl::RGBA,
698 gl::UNSIGNED_BYTE,
699 );
700 let gl_error = gl.get_error();
701 if gl_error != gl::NO_ERROR {
702 warn!("GL error code 0x{gl_error:x} set after read_pixels");
703 }
704
705 let source_rectangle = source_rectangle.to_usize();
707 let orig_pixels = pixels.clone();
708 let stride = source_rectangle.width() * 4;
709 for y in 0..source_rectangle.height() {
710 let dst_start = y * stride;
711 let src_start = (source_rectangle.height() - y - 1) * stride;
712 let src_slice = &orig_pixels[src_start..src_start + stride];
713 pixels[dst_start..dst_start + stride].clone_from_slice(&src_slice[..stride]);
714 }
715
716 RgbaImage::from_raw(
717 source_rectangle.width() as u32,
718 source_rectangle.height() as u32,
719 pixels,
720 )
721 }
722}
723
724pub struct OffscreenRenderingContext {
725 parent_context: Rc<WindowRenderingContext>,
726 size: Cell<PhysicalSize<u32>>,
727 framebuffer: RefCell<Framebuffer>,
728}
729
730type RenderToParentCallback = Box<dyn Fn(&glow::Context, Rect<i32>) + Send + Sync>;
731
732impl OffscreenRenderingContext {
733 fn new(parent_context: Rc<WindowRenderingContext>, size: PhysicalSize<u32>) -> Self {
734 assert!(
735 size.width != 0 && size.height != 0,
736 "Dimensions must be at least 1x1, got {size:?}",
737 );
738
739 let framebuffer = RefCell::new(Framebuffer::new(parent_context.gleam_gl_api(), size));
740 Self {
741 parent_context,
742 size: Cell::new(size),
743 framebuffer,
744 }
745 }
746
747 pub fn parent_context(&self) -> &WindowRenderingContext {
748 &self.parent_context
749 }
750
751 pub fn render_to_parent_callback(&self) -> Option<RenderToParentCallback> {
752 let front_framebuffer_id =
754 NonZeroU32::new(self.framebuffer.borrow().framebuffer_id).map(NativeFramebuffer)?;
755 let parent_context_framebuffer_id = self.parent_context.surfman_context.framebuffer();
756 let size = self.size.get();
757 let size = Size2D::new(size.width as i32, size.height as i32);
758 Some(Box::new(move |gl, target_rect| {
759 Self::blit_framebuffer(
760 gl,
761 Rect::new(Point2D::origin(), size.to_i32()),
762 front_framebuffer_id,
763 target_rect,
764 parent_context_framebuffer_id,
765 );
766 }))
767 }
768
769 #[expect(unsafe_code)]
770 fn blit_framebuffer(
771 gl: &glow::Context,
772 source_rect: Rect<i32>,
773 source_framebuffer_id: NativeFramebuffer,
774 target_rect: Rect<i32>,
775 target_framebuffer_id: Option<NativeFramebuffer>,
776 ) {
777 use glow::HasContext as _;
778 unsafe {
779 gl.clear_color(0.0, 0.0, 0.0, 0.0);
780 gl.scissor(
781 target_rect.origin.x,
782 target_rect.origin.y,
783 target_rect.width(),
784 target_rect.height(),
785 );
786 gl.enable(gl::SCISSOR_TEST);
787 gl.clear(gl::COLOR_BUFFER_BIT);
788 gl.disable(gl::SCISSOR_TEST);
789
790 gl.bind_framebuffer(gl::READ_FRAMEBUFFER, Some(source_framebuffer_id));
791 gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, target_framebuffer_id);
792
793 gl.blit_framebuffer(
794 source_rect.origin.x,
795 source_rect.origin.y,
796 source_rect.origin.x + source_rect.width(),
797 source_rect.origin.y + source_rect.height(),
798 target_rect.origin.x,
799 target_rect.origin.y,
800 target_rect.origin.x + target_rect.width(),
801 target_rect.origin.y + target_rect.height(),
802 gl::COLOR_BUFFER_BIT,
803 gl::NEAREST,
804 );
805 gl.bind_framebuffer(gl::FRAMEBUFFER, target_framebuffer_id);
806 }
807 }
808}
809
810impl RenderingContext for OffscreenRenderingContext {
811 fn size(&self) -> PhysicalSize<u32> {
812 self.size.get()
813 }
814
815 fn resize(&self, new_size: PhysicalSize<u32>) {
816 assert!(
817 new_size.width != 0 && new_size.height != 0,
818 "Dimensions must be at least 1x1, got {new_size:?}",
819 );
820
821 let old_size = self.size.get();
822 if old_size == new_size {
823 return;
824 }
825
826 let gl = self.parent_context.gleam_gl_api();
827 let new_framebuffer = Framebuffer::new(gl.clone(), new_size);
828
829 let old_framebuffer =
830 std::mem::replace(&mut *self.framebuffer.borrow_mut(), new_framebuffer);
831 self.size.set(new_size);
832
833 let blit_size = new_size.min(old_size);
834 let rect = Rect::new(
835 Point2D::origin(),
836 Size2D::new(blit_size.width, blit_size.height),
837 )
838 .to_i32();
839
840 let Some(old_framebuffer_id) =
841 NonZeroU32::new(old_framebuffer.framebuffer_id).map(NativeFramebuffer)
842 else {
843 return;
844 };
845 let new_framebuffer_id =
846 NonZeroU32::new(self.framebuffer.borrow().framebuffer_id).map(NativeFramebuffer);
847 Self::blit_framebuffer(
848 &self.glow_gl_api(),
849 rect,
850 old_framebuffer_id,
851 rect,
852 new_framebuffer_id,
853 );
854 }
855
856 fn prepare_for_rendering(&self) {
857 self.framebuffer.borrow().bind();
858 }
859
860 fn present(&self) {}
861
862 fn make_current(&self) -> Result<(), surfman::Error> {
863 self.parent_context.make_current()
864 }
865
866 fn gleam_gl_api(&self) -> Rc<dyn gleam::gl::Gl> {
867 self.parent_context.gleam_gl_api()
868 }
869
870 fn glow_gl_api(&self) -> Arc<glow::Context> {
871 self.parent_context.glow_gl_api()
872 }
873
874 fn create_texture(
875 &self,
876 surface: Surface,
877 ) -> Option<(SurfaceTexture, u32, UntypedSize2D<i32>)> {
878 self.parent_context.create_texture(surface)
879 }
880
881 fn destroy_texture(&self, surface_texture: SurfaceTexture) -> Option<Surface> {
882 self.parent_context.destroy_texture(surface_texture)
883 }
884
885 fn connection(&self) -> Option<Connection> {
886 self.parent_context.connection()
887 }
888
889 fn read_to_image(&self, source_rectangle: DeviceIntRect) -> Option<RgbaImage> {
890 self.framebuffer.borrow().read_to_image(source_rectangle)
891 }
892
893 fn refresh_driver(&self) -> Option<Rc<dyn RefreshDriver>> {
894 self.parent_context().refresh_driver()
895 }
896}
897
898fn print_diagnostics_information_on_context_creation_failure(
899 device: &Device,
900 desired_api: GLApi,
901 desired_version: GLVersion,
902) {
903 println!("===============================================================");
904 println!(
905 "Could not create a {desired_api:?} {:?}.{:?} context when starting Servo.",
906 desired_version.major, desired_version.minor
907 );
908
909 let version = surfman::GLVersion { major: 1, minor: 0 };
910 match device
911 .create_context_descriptor(&ContextAttributes {
912 flags: ContextAttributeFlags::empty(),
913 version,
914 })
915 .and_then(|context_descriptor| device.create_context(&context_descriptor, None))
916 {
917 Ok(mut context) => {
918 #[expect(unsafe_code)]
919 let glow_gl = unsafe {
920 glow::Context::from_loader_function(|function_name| {
921 device.get_proc_address(&context, function_name)
922 })
923 };
924
925 println!(
926 "It's likely that your version of OpenGL ({:?}.{:?}) is too old.",
927 glow_gl.version().major,
928 glow_gl.version().minor
929 );
930 println!("If not, please file a bug at https://github.com/servo/servo/issues.");
931 let _ = device.destroy_context(&mut context);
932 },
933 Err(_) => {
934 println!("Could not create any {desired_api:?} context.");
935 println!("Ensure that OpenGL is working on your system.");
936 println!("If it is, please file a bug at https://github.com/servo/servo/issues.");
937 },
938 }
939 println!("===============================================================\n");
940}
941
942#[cfg(test)]
943mod test {
944 use dpi::PhysicalSize;
945 use euclid::{Box2D, Point2D, Size2D};
946 use gleam::gl;
947 use image::Rgba;
948 use surfman::{Connection, ContextAttributeFlags, ContextAttributes, Error, GLApi, GLVersion};
949
950 use super::Framebuffer;
951 use crate::rendering_context::SoftwareRenderingContext;
952
953 #[test]
954 #[expect(unsafe_code)]
955 fn test_read_pixels() -> Result<(), Error> {
956 let connection = Connection::new()?;
957 let adapter = connection.create_software_adapter()?;
958 let device = connection.create_device(&adapter)?;
959 let context_descriptor = device.create_context_descriptor(&ContextAttributes {
960 version: GLVersion::new(3, 0),
961 flags: ContextAttributeFlags::empty(),
962 })?;
963 let mut context = device.create_context(&context_descriptor, None)?;
964
965 let gl = match connection.gl_api() {
966 GLApi::GL => unsafe { gl::GlFns::load_with(|s| device.get_proc_address(&context, s)) },
967 GLApi::GLES => unsafe {
968 gl::GlesFns::load_with(|s| device.get_proc_address(&context, s))
969 },
970 };
971
972 device.make_context_current(&context)?;
973
974 {
975 const SIZE: u32 = 16;
976 let framebuffer = Framebuffer::new(gl, PhysicalSize::new(SIZE, SIZE));
977 framebuffer.bind();
978 framebuffer
979 .gl
980 .clear_color(12.0 / 255.0, 34.0 / 255.0, 56.0 / 255.0, 78.0 / 255.0);
981 framebuffer.gl.clear(gl::COLOR_BUFFER_BIT);
982
983 let rect = Box2D::from_origin_and_size(Point2D::zero(), Size2D::new(SIZE, SIZE));
984 let img = framebuffer
985 .read_to_image(rect.to_i32())
986 .expect("Should have been able to read back image.");
987 assert_eq!(img.width(), SIZE);
988 assert_eq!(img.height(), SIZE);
989
990 let expected_pixel: Rgba<u8> = Rgba([12, 34, 56, 78]);
991 assert!(img.pixels().all(|&p| p == expected_pixel));
992 }
993
994 device.destroy_context(&mut context)?;
995
996 Ok(())
997 }
998
999 #[test]
1000 fn test_minimum_size_error() {
1001 let result = SoftwareRenderingContext::new(PhysicalSize {
1002 width: 0,
1003 height: 1,
1004 });
1005 match result {
1006 Err(surfman::Error::Failed) => (),
1007 _ => panic!("Expected {:?}", surfman::Error::Failed),
1008 }
1009 }
1010}