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