1use super::context::CurrentContextGuard;
6use super::device::EGL_FUNCTIONS;
7use crate::egl;
8use crate::egl::types::{EGLAttrib, EGLConfig, EGLContext, EGLDisplay, EGLSurface, EGLint};
9use crate::gl;
10use crate::gl_utils;
11use crate::platform::generic::egl::error::ToWindowingApiError;
12use crate::platform::generic::egl::ffi::EGLClientBuffer;
13use crate::platform::generic::egl::ffi::EGLImageKHR;
14use crate::platform::generic::egl::ffi::EGL_EXTENSION_FUNCTIONS;
15use crate::platform::generic::egl::ffi::EGL_GL_TEXTURE_2D_KHR;
16use crate::platform::generic::egl::ffi::EGL_IMAGE_PRESERVED_KHR;
17use crate::platform::generic::egl::ffi::EGL_NO_IMAGE_KHR;
18use crate::renderbuffers::Renderbuffers;
19use crate::Gl;
20use crate::{ContextAttributes, ContextID, Error, SurfaceID, SurfaceInfo};
21
22use euclid::default::Size2D;
23use glow::{Framebuffer, HasContext, PixelUnpackData, Texture};
24use std::fmt::{self, Debug, Formatter};
25use std::marker::PhantomData;
26use std::mem;
27use std::os::raw::c_void;
28use std::ptr;
29
30#[allow(dead_code)]
31#[derive(Clone)]
32pub(crate) struct ExternalEGLSurfaces {
33 pub(crate) draw: EGLSurface,
34 pub(crate) read: EGLSurface,
35}
36
37pub struct EGLBackedSurface {
38 pub(crate) context_id: ContextID,
39 pub(crate) size: Size2D<i32>,
40 pub(crate) objects: EGLSurfaceObjects,
41 pub(crate) destroyed: bool,
42}
43
44impl Debug for EGLBackedSurface {
45 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
46 write!(f, "Surface({:x})", self.id().0)
47 }
48}
49
50unsafe impl Send for EGLBackedSurface {}
51
52#[allow(dead_code)]
53pub(crate) enum EGLSurfaceObjects {
54 TextureImage {
55 egl_image: EGLImageKHR,
56 framebuffer_object: Option<Framebuffer>,
57 texture_object: Option<Texture>,
58 renderbuffers: Renderbuffers,
59 },
60 Window {
61 native_window: *const c_void,
62 egl_surface: EGLSurface,
63 },
64}
65
66pub(crate) struct EGLSurfaceTexture {
67 pub(crate) surface: EGLBackedSurface,
68 pub(crate) texture_object: Option<Texture>,
69 pub(crate) phantom: PhantomData<*const ()>,
70}
71
72impl Debug for EGLSurfaceTexture {
73 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
74 write!(f, "SurfaceTexture({:?})", self.surface)
75 }
76}
77
78impl EGLBackedSurface {
79 pub(crate) fn new_generic(
80 gl: &Gl,
81 egl_display: EGLDisplay,
82 egl_context: EGLContext,
83 context_id: ContextID,
84 context_attributes: &ContextAttributes,
85 size: &Size2D<i32>,
86 ) -> EGLBackedSurface {
87 let egl_image_attribs = [
88 EGL_IMAGE_PRESERVED_KHR as EGLint,
89 egl::FALSE as EGLint,
90 egl::NONE as EGLint,
91 0,
92 ];
93
94 unsafe {
95 let texture_object = gl.create_texture().ok();
97 let old_texture_object = gl.get_parameter_texture(gl::TEXTURE_BINDING_2D);
99 gl.bind_texture(gl::TEXTURE_2D, texture_object);
100 let unpack_buffer = gl.get_parameter_buffer(gl::PIXEL_UNPACK_BUFFER_BINDING);
104 if unpack_buffer.is_some() {
105 gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, None);
106 }
107 gl.tex_image_2d(
108 gl::TEXTURE_2D,
109 0,
110 gl::RGBA as i32,
111 size.width,
112 size.height,
113 0,
114 gl::RGBA,
115 gl::UNSIGNED_BYTE,
116 PixelUnpackData::Slice(None),
117 );
118 gl.bind_texture(gl::TEXTURE_2D, old_texture_object);
120 if unpack_buffer.is_some() {
121 gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, unpack_buffer);
122 }
123
124 let egl_client_buffer =
126 texture_object.map_or(0, |tex| tex.0.get()) as usize as EGLClientBuffer;
127 let egl_image = (EGL_EXTENSION_FUNCTIONS.CreateImageKHR)(
128 egl_display,
129 egl_context,
130 EGL_GL_TEXTURE_2D_KHR,
131 egl_client_buffer,
132 egl_image_attribs.as_ptr(),
133 );
134
135 let framebuffer_object =
137 gl_utils::create_and_bind_framebuffer(gl, gl::TEXTURE_2D, texture_object);
138
139 let renderbuffers = Renderbuffers::new(gl, size, context_attributes);
141 renderbuffers.bind_to_current_framebuffer(gl);
142
143 debug_assert_eq!(
144 gl.check_framebuffer_status(gl::FRAMEBUFFER),
145 gl::FRAMEBUFFER_COMPLETE
146 );
147
148 EGLBackedSurface {
149 context_id,
150 size: *size,
151 objects: EGLSurfaceObjects::TextureImage {
152 egl_image,
153 framebuffer_object: Some(framebuffer_object),
154 texture_object,
155 renderbuffers,
156 },
157 destroyed: false,
158 }
159 }
160 }
161
162 pub(crate) fn new_window(
163 egl_display: EGLDisplay,
164 egl_config: EGLConfig,
165 native_window: *mut c_void,
166 context_id: ContextID,
167 size: &Size2D<i32>,
168 ) -> EGLBackedSurface {
169 EGL_FUNCTIONS.with(|egl| unsafe {
170 let window_surface_attribs = [egl::NONE as EGLAttrib];
171 let egl_surface = egl.CreatePlatformWindowSurface(
172 egl_display,
173 egl_config,
174 native_window,
175 window_surface_attribs.as_ptr(),
176 );
177 assert_ne!(egl_surface, egl::NO_SURFACE);
178
179 EGLBackedSurface {
180 context_id,
181 size: *size,
182 objects: EGLSurfaceObjects::Window {
183 native_window,
184 egl_surface,
185 },
186 destroyed: false,
187 }
188 })
189 }
190
191 pub(crate) fn to_surface_texture(
192 self,
193 gl: &Gl,
194 ) -> Result<EGLSurfaceTexture, (Error, EGLBackedSurface)> {
195 unsafe {
196 let egl_image = match self.objects {
197 EGLSurfaceObjects::TextureImage { egl_image, .. } => egl_image,
198 EGLSurfaceObjects::Window { .. } => return Err((Error::WidgetAttached, self)),
199 };
200 let texture_object = bind_egl_image_to_gl_texture(gl, egl_image);
201 Ok(EGLSurfaceTexture {
202 surface: self,
203 texture_object: Some(texture_object),
204 phantom: PhantomData,
205 })
206 }
207 }
208
209 pub(crate) fn destroy(
210 &mut self,
211 gl: &Gl,
212 egl_display: EGLDisplay,
213 context_id: ContextID,
214 ) -> Result<Option<*const c_void>, Error> {
215 if context_id != self.context_id {
216 return Err(Error::IncompatibleSurface);
217 }
218
219 unsafe {
220 match self.objects {
221 EGLSurfaceObjects::TextureImage {
222 ref mut egl_image,
223 ref mut framebuffer_object,
224 ref mut texture_object,
225 ref mut renderbuffers,
226 } => {
227 gl.bind_framebuffer(gl::FRAMEBUFFER, None);
228 if let Some(framebuffer) = framebuffer_object.take() {
229 gl.delete_framebuffer(framebuffer);
230 }
231 renderbuffers.destroy(gl);
232
233 let result = (EGL_EXTENSION_FUNCTIONS.DestroyImageKHR)(egl_display, *egl_image);
234 assert_ne!(result, egl::FALSE);
235 *egl_image = EGL_NO_IMAGE_KHR;
236
237 if let Some(texture) = texture_object.take() {
238 gl.delete_texture(texture);
239 }
240
241 self.destroyed = true;
242 Ok(None)
243 }
244 EGLSurfaceObjects::Window {
245 ref mut egl_surface,
246 ref mut native_window,
247 } => {
248 EGL_FUNCTIONS.with(|egl| {
249 egl.DestroySurface(egl_display, *egl_surface);
250 *egl_surface = egl::NO_SURFACE;
251 });
252
253 self.destroyed = true;
254 Ok(Some(mem::replace(native_window, ptr::null())))
255 }
256 }
257 }
258 }
259
260 pub(crate) fn present(
262 &self,
263 egl_display: EGLDisplay,
264 egl_context: EGLContext,
265 ) -> Result<(), Error> {
266 unsafe {
267 match self.objects {
268 EGLSurfaceObjects::Window { egl_surface, .. } => {
269 let _guard = CurrentContextGuard::new();
272
273 EGL_FUNCTIONS.with(|egl| {
274 let result =
275 egl.MakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
276 if result == egl::FALSE {
277 let err = egl.GetError().to_windowing_api_error();
278 return Err(Error::MakeCurrentFailed(err));
279 }
280
281 let ok = egl.SwapBuffers(egl_display, egl_surface);
282 if ok != egl::FALSE {
283 Ok(())
284 } else {
285 Err(Error::PresentFailed(
286 egl.GetError().to_windowing_api_error(),
287 ))
288 }
289 })
290 }
291 EGLSurfaceObjects::TextureImage { .. } => Err(Error::NoWidgetAttached),
292 }
293 }
294 }
295
296 pub(crate) fn info(&self) -> SurfaceInfo {
297 SurfaceInfo {
298 size: self.size,
299 id: self.id(),
300 context_id: self.context_id,
301 framebuffer_object: match self.objects {
302 EGLSurfaceObjects::TextureImage {
303 framebuffer_object, ..
304 } => framebuffer_object,
305 EGLSurfaceObjects::Window { .. } => None,
306 },
307 }
308 }
309
310 pub(crate) fn id(&self) -> SurfaceID {
311 match self.objects {
312 EGLSurfaceObjects::TextureImage { egl_image, .. } => SurfaceID(egl_image as usize),
313 EGLSurfaceObjects::Window { egl_surface, .. } => SurfaceID(egl_surface as usize),
314 }
315 }
316
317 pub(crate) fn native_window(&self) -> Result<*const c_void, Error> {
318 match self.objects {
319 EGLSurfaceObjects::TextureImage { .. } => Err(Error::NoWidgetAttached),
320 EGLSurfaceObjects::Window { native_window, .. } => Ok(native_window),
321 }
322 }
323
324 pub(crate) fn unbind(&self, gl: &Gl, egl_display: EGLDisplay, egl_context: EGLContext) {
325 unsafe {
327 EGL_FUNCTIONS.with(|egl| {
328 if egl.GetCurrentContext() != egl_context {
329 return;
330 }
331
332 egl.MakeCurrent(egl_display, egl::NO_SURFACE, egl::NO_SURFACE, egl_context);
333
334 match self.objects {
335 EGLSurfaceObjects::TextureImage {
336 framebuffer_object: Some(framebuffer_object),
337 ..
338 } => {
339 gl_utils::unbind_framebuffer_if_necessary(gl, framebuffer_object);
340 }
341 EGLSurfaceObjects::TextureImage { .. } | EGLSurfaceObjects::Window { .. } => {}
342 }
343 })
344 }
345 }
346
347 pub(crate) fn egl_surfaces(&self) -> ExternalEGLSurfaces {
348 match self.objects {
349 EGLSurfaceObjects::Window { egl_surface, .. } => ExternalEGLSurfaces {
350 draw: egl_surface,
351 read: egl_surface,
352 },
353 EGLSurfaceObjects::TextureImage { .. } => ExternalEGLSurfaces::default(),
354 }
355 }
356}
357
358impl EGLSurfaceTexture {
359 pub(crate) fn destroy(mut self, gl: &Gl) -> EGLBackedSurface {
360 if let Some(texture) = self.texture_object.take() {
361 unsafe {
362 gl.delete_texture(texture);
363 }
364 }
365 self.surface
366 }
367}
368
369impl Default for ExternalEGLSurfaces {
370 #[inline]
371 fn default() -> ExternalEGLSurfaces {
372 ExternalEGLSurfaces {
373 draw: egl::NO_SURFACE,
374 read: egl::NO_SURFACE,
375 }
376 }
377}
378
379#[allow(dead_code)]
380pub(crate) unsafe fn create_pbuffer_surface(
381 egl_display: EGLDisplay,
382 egl_config: EGLConfig,
383 size: &Size2D<i32>,
384) -> EGLSurface {
385 let attributes = [
386 egl::WIDTH as EGLint,
387 size.width as EGLint,
388 egl::HEIGHT as EGLint,
389 size.height as EGLint,
390 egl::TEXTURE_FORMAT as EGLint,
391 egl::TEXTURE_RGBA as EGLint,
392 egl::TEXTURE_TARGET as EGLint,
393 egl::TEXTURE_2D as EGLint,
394 egl::NONE as EGLint,
395 0,
396 0,
397 0,
398 ];
399
400 EGL_FUNCTIONS.with(|egl| {
401 let egl_surface = egl.CreatePbufferSurface(egl_display, egl_config, attributes.as_ptr());
402 assert_ne!(egl_surface, egl::NO_SURFACE);
403 egl_surface
404 })
405}
406
407#[allow(dead_code)]
408pub(crate) unsafe fn bind_egl_image_to_gl_texture(gl: &Gl, egl_image: EGLImageKHR) -> Texture {
409 let texture = gl.create_texture().unwrap();
410
411 let texture_binding = gl.get_parameter_texture(gl::TEXTURE_BINDING_2D);
412
413 gl.bind_texture(gl::TEXTURE_2D, Some(texture));
415 (EGL_EXTENSION_FUNCTIONS.ImageTargetTexture2DOES)(gl::TEXTURE_2D, egl_image);
416 gl.tex_parameter_i32(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as _);
417 gl.tex_parameter_i32(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as _);
418 gl.tex_parameter_i32(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as _);
419 gl.tex_parameter_i32(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as _);
420 gl.bind_texture(gl::TEXTURE_2D, texture_binding);
421
422 debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
423 texture
424}