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