wgpu_native_texture_interop/surfman_gl/
surfman_frame_context.rs1use 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}