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 fn present(&self) {
383 let device = &mut self.surfman_rendering_info.device.borrow_mut();
384 let context = &mut self.surfman_rendering_info.context.borrow_mut();
385 let _ = self
386 .swap_chain
387 .swap_buffers(device, context, PreserveBuffer::No);
388 }
389
390 fn make_current(&self) -> Result<(), Error> {
391 self.surfman_rendering_info.make_current()
392 }
393
394 fn gleam_gl_api(&self) -> Rc<dyn gleam::gl::Gl> {
395 self.surfman_rendering_info.gleam_gl.clone()
396 }
397
398 fn glow_gl_api(&self) -> Arc<glow::Context> {
399 self.surfman_rendering_info.glow_gl.clone()
400 }
401
402 fn create_texture(
403 &self,
404 surface: Surface,
405 ) -> Option<(SurfaceTexture, u32, UntypedSize2D<i32>)> {
406 self.surfman_rendering_info.create_texture(surface)
407 }
408
409 fn destroy_texture(&self, surface_texture: SurfaceTexture) -> Option<Surface> {
410 self.surfman_rendering_info.destroy_texture(surface_texture)
411 }
412
413 fn connection(&self) -> Option<Connection> {
414 self.surfman_rendering_info.connection()
415 }
416}
417
418pub struct WindowRenderingContext {
427 size: Cell<PhysicalSize<u32>>,
429 surfman_context: SurfmanRenderingContext,
430}
431
432impl WindowRenderingContext {
433 pub fn new(
434 display_handle: DisplayHandle,
435 window_handle: WindowHandle,
436 size: PhysicalSize<u32>,
437 ) -> Result<Self, Error> {
438 Self::new_with_optional_refresh_driver(display_handle, window_handle, size, None)
439 }
440
441 pub fn new_with_refresh_driver(
442 display_handle: DisplayHandle,
443 window_handle: WindowHandle,
444 size: PhysicalSize<u32>,
445 refresh_driver: Rc<dyn RefreshDriver>,
446 ) -> Result<Self, Error> {
447 Self::new_with_optional_refresh_driver(
448 display_handle,
449 window_handle,
450 size,
451 Some(refresh_driver),
452 )
453 }
454
455 fn new_with_optional_refresh_driver(
456 display_handle: DisplayHandle,
457 window_handle: WindowHandle,
458 size: PhysicalSize<u32>,
459 refresh_driver: Option<Rc<dyn RefreshDriver>>,
460 ) -> Result<Self, Error> {
461 if size.width == 0 || size.height == 0 {
462 log::error!(
463 "Unable to create WindowRenderingContext with size under 1x1 ({size:?} provided)"
464 );
465 return Err(Error::Failed);
466 }
467
468 let connection = Connection::from_display_handle(display_handle)?;
469 let adapter = connection.create_adapter()?;
470 let surfman_context = SurfmanRenderingContext::new(&connection, &adapter, refresh_driver)?;
471
472 let native_widget = connection
473 .create_native_widget_from_window_handle(
474 window_handle,
475 Size2D::new(size.width as i32, size.height as i32),
476 )
477 .expect("Failed to create native widget");
478
479 let surface = surfman_context.create_surface(SurfaceType::Widget { native_widget })?;
480 surfman_context.bind_surface(surface)?;
481 surfman_context.make_current()?;
482
483 Ok(Self {
484 size: Cell::new(size),
485 surfman_context,
486 })
487 }
488
489 pub fn offscreen_context(
490 self: &Rc<Self>,
491 size: PhysicalSize<u32>,
492 ) -> OffscreenRenderingContext {
493 OffscreenRenderingContext::new(self.clone(), size)
494 }
495
496 pub fn take_window(&self) -> Result<(), Error> {
501 let device = self.surfman_context.device.borrow_mut();
502 let mut context = self.surfman_context.context.borrow_mut();
503 let mut surface = device.unbind_surface_from_context(&mut context)?.unwrap();
504 device.destroy_surface(&mut context, &mut surface)?;
505 Ok(())
506 }
507
508 pub fn set_window(
513 &self,
514 window_handle: WindowHandle,
515 size: PhysicalSize<u32>,
516 ) -> Result<(), Error> {
517 let device = self.surfman_context.device.borrow_mut();
518 let mut context = self.surfman_context.context.borrow_mut();
519
520 let native_widget = device
521 .connection()
522 .create_native_widget_from_window_handle(
523 window_handle,
524 Size2D::new(size.width as i32, size.height as i32),
525 )
526 .expect("Failed to create native widget");
527
528 let surface_access = SurfaceAccess::GPUOnly;
529 let surface_type = SurfaceType::Widget { native_widget };
530 let surface = device.create_surface(&context, surface_access, surface_type)?;
531
532 device
533 .bind_surface_to_context(&mut context, surface)
534 .map_err(|(err, mut surface)| {
535 let _ = device.destroy_surface(&mut context, &mut surface);
536 err
537 })?;
538 device.make_context_current(&context)?;
539 Ok(())
540 }
541
542 pub fn surfman_details(&self) -> (RefMut<'_, Device>, RefMut<'_, Context>) {
543 (
544 self.surfman_context.device.borrow_mut(),
545 self.surfman_context.context.borrow_mut(),
546 )
547 }
548}
549
550impl RenderingContext for WindowRenderingContext {
551 fn prepare_for_rendering(&self) {
552 self.surfman_context.prepare_for_rendering();
553 }
554
555 fn read_to_image(&self, source_rectangle: DeviceIntRect) -> Option<RgbaImage> {
556 self.surfman_context.read_to_image(source_rectangle)
557 }
558
559 fn size(&self) -> PhysicalSize<u32> {
560 self.size.get()
561 }
562
563 fn resize(&self, size: PhysicalSize<u32>) {
564 match self.surfman_context.resize_surface(size) {
565 Ok(..) => self.size.set(size),
566 Err(error) => warn!("Error resizing surface: {error:?}"),
567 }
568 }
569
570 fn present(&self) {
571 if let Err(error) = self.surfman_context.present_bound_surface() {
572 warn!("Error presenting surface: {error:?}");
573 }
574 }
575
576 fn make_current(&self) -> Result<(), Error> {
577 self.surfman_context.make_current()
578 }
579
580 fn gleam_gl_api(&self) -> Rc<dyn gleam::gl::Gl> {
581 self.surfman_context.gleam_gl.clone()
582 }
583
584 fn glow_gl_api(&self) -> Arc<glow::Context> {
585 self.surfman_context.glow_gl.clone()
586 }
587
588 fn create_texture(
589 &self,
590 surface: Surface,
591 ) -> Option<(SurfaceTexture, u32, UntypedSize2D<i32>)> {
592 self.surfman_context.create_texture(surface)
593 }
594
595 fn destroy_texture(&self, surface_texture: SurfaceTexture) -> Option<Surface> {
596 self.surfman_context.destroy_texture(surface_texture)
597 }
598
599 fn connection(&self) -> Option<Connection> {
600 self.surfman_context.connection()
601 }
602
603 fn refresh_driver(&self) -> Option<Rc<dyn RefreshDriver>> {
604 self.surfman_context.refresh_driver()
605 }
606}
607
608struct Framebuffer {
609 gl: Rc<dyn Gl>,
610 framebuffer_id: gl::GLuint,
611 renderbuffer_id: gl::GLuint,
612 texture_id: gl::GLuint,
613}
614
615impl Framebuffer {
616 fn bind(&self) {
617 trace!("Binding FBO {}", self.framebuffer_id);
618 self.gl
619 .bind_framebuffer(gl::FRAMEBUFFER, self.framebuffer_id)
620 }
621}
622
623impl Drop for Framebuffer {
624 fn drop(&mut self) {
625 self.gl.bind_framebuffer(gl::FRAMEBUFFER, 0);
626 self.gl.delete_textures(&[self.texture_id]);
627 self.gl.delete_renderbuffers(&[self.renderbuffer_id]);
628 self.gl.delete_framebuffers(&[self.framebuffer_id]);
629 }
630}
631
632impl Framebuffer {
633 fn new(gl: Rc<dyn Gl>, size: PhysicalSize<u32>) -> Self {
634 let framebuffer_ids = gl.gen_framebuffers(1);
635 gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer_ids[0]);
636
637 let texture_ids = gl.gen_textures(1);
638 gl.bind_texture(gl::TEXTURE_2D, texture_ids[0]);
639 gl.tex_image_2d(
640 gl::TEXTURE_2D,
641 0,
642 gl::RGBA as gl::GLint,
643 size.width as gl::GLsizei,
644 size.height as gl::GLsizei,
645 0,
646 gl::RGBA,
647 gl::UNSIGNED_BYTE,
648 None,
649 );
650 gl.tex_parameter_i(
651 gl::TEXTURE_2D,
652 gl::TEXTURE_MAG_FILTER,
653 gl::NEAREST as gl::GLint,
654 );
655 gl.tex_parameter_i(
656 gl::TEXTURE_2D,
657 gl::TEXTURE_MIN_FILTER,
658 gl::NEAREST as gl::GLint,
659 );
660
661 gl.framebuffer_texture_2d(
662 gl::FRAMEBUFFER,
663 gl::COLOR_ATTACHMENT0,
664 gl::TEXTURE_2D,
665 texture_ids[0],
666 0,
667 );
668
669 gl.bind_texture(gl::TEXTURE_2D, 0);
670
671 let renderbuffer_ids = gl.gen_renderbuffers(1);
672 let depth_rb = renderbuffer_ids[0];
673 gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb);
674 gl.renderbuffer_storage(
675 gl::RENDERBUFFER,
676 gl::DEPTH_COMPONENT24,
677 size.width as gl::GLsizei,
678 size.height as gl::GLsizei,
679 );
680 gl.framebuffer_renderbuffer(
681 gl::FRAMEBUFFER,
682 gl::DEPTH_ATTACHMENT,
683 gl::RENDERBUFFER,
684 depth_rb,
685 );
686
687 Self {
688 gl,
689 framebuffer_id: *framebuffer_ids
690 .first()
691 .expect("Guaranteed by GL operations"),
692 renderbuffer_id: *renderbuffer_ids
693 .first()
694 .expect("Guaranteed by GL operations"),
695 texture_id: *texture_ids.first().expect("Guaranteed by GL operations"),
696 }
697 }
698
699 fn read_to_image(&self, source_rectangle: DeviceIntRect) -> Option<RgbaImage> {
700 Self::read_framebuffer_to_image(&self.gl, self.framebuffer_id, source_rectangle)
701 }
702
703 fn read_framebuffer_to_image(
704 gl: &Rc<dyn Gl>,
705 framebuffer_id: u32,
706 source_rectangle: DeviceIntRect,
707 ) -> Option<RgbaImage> {
708 gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer_id);
709
710 gl.bind_vertex_array(0);
717
718 let mut pixels = gl.read_pixels(
719 source_rectangle.min.x,
720 source_rectangle.min.y,
721 source_rectangle.width(),
722 source_rectangle.height(),
723 gl::RGBA,
724 gl::UNSIGNED_BYTE,
725 );
726 let gl_error = gl.get_error();
727 if gl_error != gl::NO_ERROR {
728 warn!("GL error code 0x{gl_error:x} set after read_pixels");
729 }
730
731 let source_rectangle = source_rectangle.to_usize();
733 let orig_pixels = pixels.clone();
734 let stride = source_rectangle.width() * 4;
735 for y in 0..source_rectangle.height() {
736 let dst_start = y * stride;
737 let src_start = (source_rectangle.height() - y - 1) * stride;
738 let src_slice = &orig_pixels[src_start..src_start + stride];
739 pixels[dst_start..dst_start + stride].clone_from_slice(&src_slice[..stride]);
740 }
741
742 RgbaImage::from_raw(
743 source_rectangle.width() as u32,
744 source_rectangle.height() as u32,
745 pixels,
746 )
747 }
748}
749
750pub struct OffscreenRenderingContext {
751 parent_context: Rc<WindowRenderingContext>,
752 size: Cell<PhysicalSize<u32>>,
753 framebuffer: RefCell<Framebuffer>,
754}
755
756type RenderToParentCallback = Box<dyn Fn(&glow::Context, Rect<i32>) + Send + Sync>;
757
758impl OffscreenRenderingContext {
759 fn new(parent_context: Rc<WindowRenderingContext>, size: PhysicalSize<u32>) -> Self {
760 assert!(
761 size.width != 0 && size.height != 0,
762 "Dimensions must be at least 1x1, got {size:?}",
763 );
764
765 let framebuffer = RefCell::new(Framebuffer::new(parent_context.gleam_gl_api(), size));
766 Self {
767 parent_context,
768 size: Cell::new(size),
769 framebuffer,
770 }
771 }
772
773 pub fn parent_context(&self) -> &WindowRenderingContext {
774 &self.parent_context
775 }
776
777 pub fn render_to_parent_callback(&self) -> Option<RenderToParentCallback> {
778 let front_framebuffer_id =
780 NonZeroU32::new(self.framebuffer.borrow().framebuffer_id).map(NativeFramebuffer)?;
781 let parent_context_framebuffer_id = self.parent_context.surfman_context.framebuffer();
782 let size = self.size.get();
783 let size = Size2D::new(size.width as i32, size.height as i32);
784 Some(Box::new(move |gl, target_rect| {
785 Self::blit_framebuffer(
786 gl,
787 Rect::new(Point2D::origin(), size.to_i32()),
788 front_framebuffer_id,
789 target_rect,
790 parent_context_framebuffer_id,
791 );
792 }))
793 }
794
795 #[expect(unsafe_code)]
796 fn blit_framebuffer(
797 gl: &glow::Context,
798 source_rect: Rect<i32>,
799 source_framebuffer_id: NativeFramebuffer,
800 target_rect: Rect<i32>,
801 target_framebuffer_id: Option<NativeFramebuffer>,
802 ) {
803 use glow::HasContext as _;
804 unsafe {
805 gl.clear_color(0.0, 0.0, 0.0, 0.0);
806 gl.scissor(
807 target_rect.origin.x,
808 target_rect.origin.y,
809 target_rect.width(),
810 target_rect.height(),
811 );
812 gl.enable(gl::SCISSOR_TEST);
813 gl.clear(gl::COLOR_BUFFER_BIT);
814 gl.disable(gl::SCISSOR_TEST);
815
816 gl.bind_framebuffer(gl::READ_FRAMEBUFFER, Some(source_framebuffer_id));
817 gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, target_framebuffer_id);
818
819 gl.blit_framebuffer(
820 source_rect.origin.x,
821 source_rect.origin.y,
822 source_rect.origin.x + source_rect.width(),
823 source_rect.origin.y + source_rect.height(),
824 target_rect.origin.x,
825 target_rect.origin.y,
826 target_rect.origin.x + target_rect.width(),
827 target_rect.origin.y + target_rect.height(),
828 gl::COLOR_BUFFER_BIT,
829 gl::NEAREST,
830 );
831 gl.bind_framebuffer(gl::FRAMEBUFFER, target_framebuffer_id);
832 }
833 }
834}
835
836impl RenderingContext for OffscreenRenderingContext {
837 fn size(&self) -> PhysicalSize<u32> {
838 self.size.get()
839 }
840
841 fn resize(&self, new_size: PhysicalSize<u32>) {
842 assert!(
843 new_size.width != 0 && new_size.height != 0,
844 "Dimensions must be at least 1x1, got {new_size:?}",
845 );
846
847 let old_size = self.size.get();
848 if old_size == new_size {
849 return;
850 }
851
852 let gl = self.parent_context.gleam_gl_api();
853 let new_framebuffer = Framebuffer::new(gl.clone(), new_size);
854
855 let old_framebuffer =
856 std::mem::replace(&mut *self.framebuffer.borrow_mut(), new_framebuffer);
857 self.size.set(new_size);
858
859 let blit_size = new_size.min(old_size);
860 let rect = Rect::new(
861 Point2D::origin(),
862 Size2D::new(blit_size.width, blit_size.height),
863 )
864 .to_i32();
865
866 let Some(old_framebuffer_id) =
867 NonZeroU32::new(old_framebuffer.framebuffer_id).map(NativeFramebuffer)
868 else {
869 return;
870 };
871 let new_framebuffer_id =
872 NonZeroU32::new(self.framebuffer.borrow().framebuffer_id).map(NativeFramebuffer);
873 Self::blit_framebuffer(
874 &self.glow_gl_api(),
875 rect,
876 old_framebuffer_id,
877 rect,
878 new_framebuffer_id,
879 );
880 }
881
882 fn prepare_for_rendering(&self) {
883 self.framebuffer.borrow().bind();
884 }
885
886 fn present(&self) {}
887
888 fn make_current(&self) -> Result<(), surfman::Error> {
889 self.parent_context.make_current()
890 }
891
892 fn gleam_gl_api(&self) -> Rc<dyn gleam::gl::Gl> {
893 self.parent_context.gleam_gl_api()
894 }
895
896 fn glow_gl_api(&self) -> Arc<glow::Context> {
897 self.parent_context.glow_gl_api()
898 }
899
900 fn create_texture(
901 &self,
902 surface: Surface,
903 ) -> Option<(SurfaceTexture, u32, UntypedSize2D<i32>)> {
904 self.parent_context.create_texture(surface)
905 }
906
907 fn destroy_texture(&self, surface_texture: SurfaceTexture) -> Option<Surface> {
908 self.parent_context.destroy_texture(surface_texture)
909 }
910
911 fn connection(&self) -> Option<Connection> {
912 self.parent_context.connection()
913 }
914
915 fn read_to_image(&self, source_rectangle: DeviceIntRect) -> Option<RgbaImage> {
916 self.framebuffer.borrow().read_to_image(source_rectangle)
917 }
918
919 fn refresh_driver(&self) -> Option<Rc<dyn RefreshDriver>> {
920 self.parent_context().refresh_driver()
921 }
922}
923
924fn print_diagnostics_information_on_context_creation_failure(
925 device: &Device,
926 desired_api: GLApi,
927 desired_version: GLVersion,
928) {
929 println!("===============================================================");
930 println!(
931 "Could not create a {desired_api:?} {:?}.{:?} context when starting Servo.",
932 desired_version.major, desired_version.minor
933 );
934
935 let version = surfman::GLVersion { major: 1, minor: 0 };
936 match device
937 .create_context_descriptor(&ContextAttributes {
938 flags: ContextAttributeFlags::empty(),
939 version,
940 })
941 .and_then(|context_descriptor| device.create_context(&context_descriptor, None))
942 {
943 Ok(mut context) => {
944 #[expect(unsafe_code)]
945 let glow_gl = unsafe {
946 glow::Context::from_loader_function(|function_name| {
947 device.get_proc_address(&context, function_name)
948 })
949 };
950
951 println!(
952 "It's likely that your version of OpenGL ({:?}.{:?}) is too old.",
953 glow_gl.version().major,
954 glow_gl.version().minor
955 );
956 println!("If not, please file a bug at https://github.com/servo/servo/issues.");
957 let _ = device.destroy_context(&mut context);
958 },
959 Err(_) => {
960 println!("Could not create any {desired_api:?} context.");
961 println!("Ensure that OpenGL is working on your system.");
962 println!("If it is, please file a bug at https://github.com/servo/servo/issues.");
963 },
964 }
965 println!("===============================================================\n");
966}
967
968#[cfg(test)]
969mod test {
970 use dpi::PhysicalSize;
971 use euclid::{Box2D, Point2D, Size2D};
972 use gleam::gl;
973 use image::Rgba;
974 use surfman::{Connection, ContextAttributeFlags, ContextAttributes, Error, GLApi, GLVersion};
975
976 use super::Framebuffer;
977 use crate::rendering_context::SoftwareRenderingContext;
978
979 #[test]
980 #[expect(unsafe_code)]
981 fn test_read_pixels() -> Result<(), Error> {
982 let connection = Connection::new()?;
983 let adapter = connection.create_software_adapter()?;
984 let device = connection.create_device(&adapter)?;
985 let context_descriptor = device.create_context_descriptor(&ContextAttributes {
986 version: GLVersion::new(3, 0),
987 flags: ContextAttributeFlags::empty(),
988 })?;
989 let mut context = device.create_context(&context_descriptor, None)?;
990
991 let gl = match connection.gl_api() {
992 GLApi::GL => unsafe { gl::GlFns::load_with(|s| device.get_proc_address(&context, s)) },
993 GLApi::GLES => unsafe {
994 gl::GlesFns::load_with(|s| device.get_proc_address(&context, s))
995 },
996 };
997
998 device.make_context_current(&context)?;
999
1000 {
1001 const SIZE: u32 = 16;
1002 let framebuffer = Framebuffer::new(gl, PhysicalSize::new(SIZE, SIZE));
1003 framebuffer.bind();
1004 framebuffer
1005 .gl
1006 .clear_color(12.0 / 255.0, 34.0 / 255.0, 56.0 / 255.0, 78.0 / 255.0);
1007 framebuffer.gl.clear(gl::COLOR_BUFFER_BIT);
1008
1009 let rect = Box2D::from_origin_and_size(Point2D::zero(), Size2D::new(SIZE, SIZE));
1010 let img = framebuffer
1011 .read_to_image(rect.to_i32())
1012 .expect("Should have been able to read back image.");
1013 assert_eq!(img.width(), SIZE);
1014 assert_eq!(img.height(), SIZE);
1015
1016 let expected_pixel: Rgba<u8> = Rgba([12, 34, 56, 78]);
1017 assert!(img.pixels().all(|&p| p == expected_pixel));
1018 }
1019
1020 device.destroy_context(&mut context)?;
1021
1022 Ok(())
1023 }
1024
1025 #[test]
1026 fn test_minimum_size_error() {
1027 let result = SoftwareRenderingContext::new(PhysicalSize {
1028 width: 0,
1029 height: 1,
1030 });
1031 match result {
1032 Err(surfman::Error::Failed) => (),
1033 _ => panic!("Expected {:?}", surfman::Error::Failed),
1034 }
1035 }
1036}