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