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