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