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