Skip to main content

wgpu_native_texture_interop/surfman_gl/
surfman_frame_context.rs

1use std::{cell::RefCell, num::NonZeroU32, rc::Rc, sync::Arc};
2
3use euclid::default::Size2D;
4use gleam::gl::{self, Gl};
5use glow::NativeFramebuffer;
6use image::RgbaImage;
7use surfman::{
8    Adapter, Connection, Context, ContextAttributeFlags, ContextAttributes, Device, Error, GLApi,
9    NativeWidget, Surface, SurfaceAccess, SurfaceInfo, SurfaceTexture, SurfaceType,
10    chains::SwapChain,
11};
12
13pub struct SurfmanFrameContext {
14    pub gleam_gl: Rc<dyn Gl>,
15    pub glow_gl: Arc<glow::Context>,
16    pub device: RefCell<Device>,
17    pub context: RefCell<Context>,
18}
19
20impl Drop for SurfmanFrameContext {
21    fn drop(&mut self) {
22        let device = &mut self.device.borrow_mut();
23        let context = &mut self.context.borrow_mut();
24        let _ = device.destroy_context(context);
25    }
26}
27
28impl SurfmanFrameContext {
29    pub fn new(connection: &Connection, adapter: &Adapter) -> Result<Self, Error> {
30        let device = connection.create_device(adapter)?;
31
32        let flags = ContextAttributeFlags::ALPHA
33            | ContextAttributeFlags::DEPTH
34            | ContextAttributeFlags::STENCIL;
35        let gl_api = connection.gl_api();
36        let version = match &gl_api {
37            GLApi::GLES => surfman::GLVersion { major: 3, minor: 0 },
38            GLApi::GL => surfman::GLVersion { major: 4, minor: 5 },
39        };
40        let context_descriptor =
41            device.create_context_descriptor(&ContextAttributes { flags, version })?;
42        let context = device.create_context(&context_descriptor, None)?;
43
44        let gleam_gl = match gl_api {
45            GLApi::GL => unsafe {
46                gl::GlFns::load_with(|func_name| device.get_proc_address(&context, func_name))
47            },
48            GLApi::GLES => unsafe {
49                gl::GlesFns::load_with(|func_name| device.get_proc_address(&context, func_name))
50            },
51        };
52
53        let glow_gl = unsafe {
54            glow::Context::from_loader_function(|function_name| {
55                device.get_proc_address(&context, function_name)
56            })
57        };
58
59        Ok(Self {
60            gleam_gl,
61            glow_gl: Arc::new(glow_gl),
62            device: RefCell::new(device),
63            context: RefCell::new(context),
64        })
65    }
66
67    pub fn create_surface(
68        &self,
69        surface_type: SurfaceType<NativeWidget>,
70    ) -> Result<Surface, Error> {
71        let device = &mut self.device.borrow_mut();
72        let context = &self.context.borrow();
73        device.create_surface(context, SurfaceAccess::GPUOnly, surface_type)
74    }
75
76    pub fn bind_surface(&self, surface: Surface) -> Result<(), Error> {
77        let device = &self.device.borrow();
78        let context = &mut self.context.borrow_mut();
79        device
80            .bind_surface_to_context(context, surface)
81            .map_err(|(err, mut surface)| {
82                let _ = device.destroy_surface(context, &mut surface);
83                err
84            })?;
85        Ok(())
86    }
87
88    pub fn unbind_surface(&self) -> Result<Option<Surface>, Error> {
89        let device = &self.device.borrow();
90        let context = &mut self.context.borrow_mut();
91        device.unbind_surface_from_context(context)
92    }
93
94    pub fn create_attached_swap_chain(&self) -> Result<SwapChain<Device>, Error> {
95        let device = &mut self.device.borrow_mut();
96        let context = &mut self.context.borrow_mut();
97        SwapChain::create_attached(device, context, SurfaceAccess::GPUOnly)
98    }
99
100    pub fn make_current(&self) -> Result<(), Error> {
101        let device = &self.device.borrow();
102        let context = &mut self.context.borrow_mut();
103        device.make_context_current(context)
104    }
105
106    pub fn prepare_for_rendering(&self) {
107        let framebuffer_id = self
108            .framebuffer()
109            .map_or(0, |framebuffer| framebuffer.0.into());
110        self.gleam_gl
111            .bind_framebuffer(gleam::gl::FRAMEBUFFER, framebuffer_id);
112    }
113
114    pub fn read_to_image_region(
115        &self,
116        x: i32,
117        y: i32,
118        width: i32,
119        height: i32,
120    ) -> Option<RgbaImage> {
121        let framebuffer_id = self
122            .framebuffer()
123            .map_or(0, |framebuffer| framebuffer.0.into());
124        Self::read_framebuffer_to_image(&self.gleam_gl, framebuffer_id, x, y, width, height)
125    }
126
127    pub fn create_texture(&self, surface: Surface) -> Option<(SurfaceTexture, u32, Size2D<i32>)> {
128        let device = &self.device.borrow();
129        let context = &mut self.context.borrow_mut();
130
131        let SurfaceInfo {
132            id: _front_buffer_id,
133            size,
134            ..
135        } = device.surface_info(&surface);
136        let surface_texture = device.create_surface_texture(context, surface).ok()?;
137        let gl_texture = device
138            .surface_texture_object(&surface_texture)
139            .map(|tex| tex.0.get())
140            .unwrap_or(0);
141
142        Some((surface_texture, gl_texture, size))
143    }
144
145    pub fn destroy_texture(&self, surface_texture: SurfaceTexture) -> Option<Surface> {
146        let device = &self.device.borrow();
147        let context = &mut self.context.borrow_mut();
148
149        device
150            .destroy_surface_texture(context, surface_texture)
151            .map_err(|(error, _)| error)
152            .ok()
153    }
154
155    pub fn connection(&self) -> Option<Connection> {
156        Some(self.device.borrow().connection())
157    }
158
159    fn framebuffer(&self) -> Option<NativeFramebuffer> {
160        let device = &self.device.borrow();
161        let context = &self.context.borrow();
162        device
163            .context_surface_info(context)
164            .unwrap_or(None)
165            .and_then(|info| info.framebuffer_object)
166            .and_then(|framebuffer| NonZeroU32::new(framebuffer.0.get()))
167            .map(NativeFramebuffer)
168    }
169
170    fn read_framebuffer_to_image(
171        gl: &Rc<dyn Gl>,
172        framebuffer_id: u32,
173        x: i32,
174        y: i32,
175        width: i32,
176        height: i32,
177    ) -> Option<RgbaImage> {
178        gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer_id);
179        gl.bind_vertex_array(0);
180
181        let mut pixels = gl.read_pixels(x, y, width, height, gl::RGBA, gl::UNSIGNED_BYTE);
182
183        if gl.get_error() != gl::NO_ERROR {
184            return None;
185        }
186
187        let width = width as usize;
188        let height = height as usize;
189        let orig_pixels = pixels.clone();
190        let stride = width * 4;
191        for row in 0..height {
192            let dst_start = row * stride;
193            let src_start = (height - row - 1) * stride;
194            let src_slice = &orig_pixels[src_start..src_start + stride];
195            pixels[dst_start..dst_start + stride].clone_from_slice(src_slice);
196        }
197
198        RgbaImage::from_raw(width as u32, height as u32, pixels)
199    }
200}