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