Skip to content

Conversation

@ZengGengSen
Copy link

Connections

Description

In some embedded Linux systems, the EGL+GLES libraries cannot have their addresses obtained through eglGetProcAddress; instead, the symbol table of the library must be directly accessed (for instance, PowerVR Rogue GE8300, and some Mali GPUs).

Testing
Explain how this change is tested.

Squash or Rebase?
Squash

Checklist

  • Run cargo fmt.
  • Run taplo format.
  • Run cargo clippy --tests. If applicable, add:
    • --target wasm32-unknown-unknown
  • Run cargo xtask test to run tests.

@inner-daemons
Copy link
Collaborator

Thanks for this! Is there a related issue, preferably with logs of these failures on the devices you mentioned?

Also, just a side note, we are switching away from manually using EGL/WGL into using glutin, so unless this is also a problem there, expect this to be fixed as a side-effect at some point. I don't however know on what timeframe the switch to glutin will happen.

@ZengGengSen
Copy link
Author

@inner-daemons You can take a look at my other pr(#8809). It roughly initializes the OpenGL environment using glutin. This should be helpful for porting to glutin. Moreover, glutin also has this issue. The error message for this problem is that the address obtained from eglGetProcAddress for glGetString is null.

@inner-daemons
Copy link
Collaborator

inner-daemons commented Jan 4, 2026

@ZengGengSen You haven't yet provided any logs about this supposed error. Until you do so this PR won't be considered for merging. Please file an issue with logs and steps to reproduce.

I'd also be very surprised if your glutin PR is merged though I haven't taken a close look yet. I know orher maintainers were planning to tackle this themselves.

@ZengGengSen
Copy link
Author

@inner-daemons
This PR actually consists of two issues. One is the failure of the choose_config function (this issue will be fixed along with the merge of glutin), and the other is the failure in obtaining the pointer (this issue also exists in glutin).

I want to wait for the results from glutin before addressing the pointer fetching issue (either by merging my pr, or if the dev team has a better idea).

A new pull request will be created at that time to address the issue with the pointers.

The solutions to some of the issues in my glutin's pr are not very good. There are some parts that are not particularly elegant. This situation is related to the conflict between the architecture of wgpu and that of glutin. If we want to use the code in this pr, it might require certain modifications to the structure of wgpu. This aspect is likely to require research by other developers who have a better understanding of this area.

Hope this can be of some help to the development team

@inner-daemons
Copy link
Collaborator

@ZengGengSen Neither PR will be discussed at all until you can provide logs of the errors. Paraphrasing in text doesn't count.

@inner-daemons
Copy link
Collaborator

Also, can I ask you. For what tasks (if any) are you using LLMs or AI like ChatGPT? Are these errors discovered by such a tool? Is the code written by such a tool? Are you using one for translating?

@ZengGengSen
Copy link
Author

ZengGengSen commented Jan 5, 2026

At the beginning, I was using it on my embedded Linux. so I wrote code.

fn run_with_fbdev() -> Result<(), Box<dyn std::error::Error>> {
    use std::{ffi, ptr, sync::Arc};

    use glutin_egl_sys::egl;
    use raw_window_handle as rwh;

    #[derive(Debug)]
    struct Window {
        display: rwh::RawDisplayHandle,
        window: rwh::RawWindowHandle,
    }

    unsafe impl Send for Window {}
    unsafe impl Sync for Window {}

    impl rwh::HasDisplayHandle for Window {
        fn display_handle(&self) -> Result<rwh::DisplayHandle<'_>, rwh::HandleError> {
            Ok(unsafe { rwh::DisplayHandle::borrow_raw(self.display) })
        }
    }

    impl rwh::HasWindowHandle for Window {
        fn window_handle(&self) -> Result<rwh::WindowHandle<'_>, rwh::HandleError> {
            Ok(unsafe { rwh::WindowHandle::borrow_raw(self.window) })
        }
    }

    let raw_display = rwh::GbmDisplayHandle::new(unsafe {
        ptr::NonNull::new_unchecked(egl::DEFAULT_DISPLAY as *mut ffi::c_void)
    })
    .into();

    let raw_window =
        rwh::GbmWindowHandle::new(unsafe { ptr::NonNull::new_unchecked(ptr::null_mut()) }).into();

    let window = Arc::new(Window {
        display: raw_display,
        window: raw_window,
    });

    let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
        backends: wgpu::Backends::GL,
        display: Some(Box::new(window.clone())),
        backend_options: wgpu::BackendOptions {
            gl: wgpu::GlBackendOptions {
                ..Default::default()
            },
            ..Default::default()
        },
        ..Default::default()
    });

    let surface = instance
        .create_surface(window)
        .expect("Cannot create surface");

    let renderer =
        pollster::block_on(renderer::WGPURenderer::new(&instance, &surface, 720, 720)).unwrap();

    loop {
        renderer.render(&surface).unwrap();
        // std::thread::sleep(std::time::Duration::from_millis(16));
    }

    Ok(())
}

The Result is

> adb shell /fbgpu

[1970-03-03T01:23:46Z DEBUG wgpu_hal::gles::egl] Client extensions: []
[1970-03-03T01:23:46Z DEBUG wgpu_hal::gles::egl] No (or unknown) windowing system (Some(Gbm(GbmDisplayHandle { gbm_device: 0x0 })), None) and EGL_MESA_platform_surfaceless not available. Using default platform
[1970-03-03T01:23:46Z DEBUG wgpu_hal::gles::egl] Display vendor "Imagination Technologies", version (1, 4)
[1970-03-03T01:23:46Z DEBUG wgpu_hal::gles::egl] Display extensions: [
        "EGL_KHR_image",
        "EGL_KHR_image_base",
        "EGL_KHR_gl_texture_2D_image",
        "EGL_KHR_gl_texture_cubemap_image",
        "EGL_KHR_gl_texture_3D_image",
        "EGL_KHR_gl_renderbuffer_image",
        "EGL_IMG_cl_image",
        "EGL_KHR_fence_sync",
        "EGL_KHR_wait_sync",
        "EGL_EXT_create_context_robustness",
        "EGL_IMG_image_plane_attribs",
        "EGL_EXT_swap_buffers_with_damage",
        "EGL_KHR_swap_buffers_with_damage",
        "EGL_KHR_partial_update",
        "EGL_EXT_buffer_age",
        "EGL_EXT_image_dma_buf_import",
        "EGL_EXT_image_dma_buf_import_modifiers",
        "EGL_EXT_yuv_surface",
        "EGL_IMG_context_priority",
        "EGL_KHR_create_context",
        "EGL_KHR_surfaceless_context",
        "EGL_KHR_no_config_context",
        "EGL_KHR_gl_colorspace",
        "EGL_EXT_image_gl_colorspace",
    ]
[1970-03-03T01:23:46Z DEBUG wgpu_hal::gles::egl]        EGL surface: +srgb khr
[1970-03-03T01:23:46Z DEBUG wgpu_hal::gles::egl]        Trying native-render
[1970-03-03T01:23:46Z DEBUG wgpu_hal::gles::egl] No config found!
[1970-03-03T01:23:46Z DEBUG wgpu_hal::gles::egl]        Trying presentation
[1970-03-03T01:23:46Z INFO  wgpu_hal::gles::egl] EGL says it can present to the window but not natively
[1970-03-03T01:23:46Z DEBUG wgpu_hal::gles::egl]        EGL context: +robust access EXT
[1970-03-03T01:23:46Z DEBUG wgpu_hal::gles::egl]        EGL context: +surfaceless
[1970-03-03T01:23:46Z DEBUG wgpu_core::instance] Instance::new: created Gl backend
[1970-03-03T01:23:46Z DEBUG wgpu_core::instance] Instance::create_surface: failed to create surface for Gl: InstanceError { message: "unsupported window: (Gbm(GbmWindowHandle { gbm_surface: 0x0 }), Gbm(GbmDisplayHandle { gbm_device: 0x0 }))", source: None }

thread 'main' (8200) panicked at src/main.rs:142:10:
Cannot create surface: CreateSurfaceError { inner: Hal(FailedToCreateSurfaceForAnyBackend({Gl: InstanceError { message: "unsupported window: (Gbm(GbmWindowHandle { gbm_surface: 0x0 }), Gbm(GbmDisplayHandle { gbm_device: 0x0 }))", source: None }})) }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

@ZengGengSen
Copy link
Author

ZengGengSen commented Jan 5, 2026

So I Directly add gbm support in the code

diff --git a/wgpu-hal/src/gles/egl.rs b/wgpu-hal/src/gles/egl.rs
index 606d1b2d4..c565d0cfb 100644
--- a/wgpu-hal/src/gles/egl.rs
+++ b/wgpu-hal/src/gles/egl.rs
@@ -814,6 +814,14 @@ impl crate::Instance for Instance {
                 .map_err(instance_err("failed to get Angle display"))?;
                 (display, WindowKind::AngleX11)
             }
+            (Some(Rdh::Gbm(display)), _) => {
+                log::debug!("Using GBM platform");
+                let display = unsafe { egl.get_display(display.gbm_device.as_ptr() as *mut _) }
+                    .ok_or_else(|| {
+                        crate::InstanceError::new("Failed to get default display".into())
+                    })?;
+                (display, WindowKind::Unknown)
+            }
             (Some(Rdh::Xcb(_xcb_display_handle)), Some(_egl)) => todo!("xcb"),
             x if client_ext_str.contains("EGL_MESA_platform_surfaceless") => {
                 log::debug!(
@@ -937,6 +945,7 @@ impl crate::Instance for Instance {
             (Rwh::Wayland(_), _) => {}
             #[cfg(Emscripten)]
             (Rwh::Web(_), _) => {}
+            (Rwh::Gbm(_), _) => {}
             other => {
                 return Err(crate::InstanceError::new(format!(
                     "unsupported window: {other:?}"
@@ -1261,6 +1270,9 @@ impl crate::Surface for Surface {
                         };
                         window_ptr
                     }
+                    (WindowKind::Unknown, Rwh::Gbm(handle)) => {
+                        handle.gbm_surface.as_ptr() as *mut ffi::c_void
+                    }
                     _ => {
                         log::warn!(
                             "Initialized platform {:?} doesn't work with window {:?}",

The Result is
It will directly report an glGetString error.

> adb shell RUST_LOG=debug /fbgpu
[1970-03-03T01:31:00Z DEBUG wgpu_hal::gles::egl] Client extensions: []
[1970-03-03T01:31:00Z DEBUG wgpu_hal::gles::egl] No (or unknown) windowing system (Some(Gbm(GbmDisplayHandle { gbm_device: 0x0 })), None) and EGL_MESA_platform_surfaceless not available. Using default platform
[1970-03-03T01:31:00Z DEBUG wgpu_hal::gles::egl] Display vendor "Imagination Technologies", version (1, 4)
[1970-03-03T01:31:00Z DEBUG wgpu_hal::gles::egl] Display extensions: [
        "EGL_KHR_image",
        "EGL_KHR_image_base",
        "EGL_KHR_gl_texture_2D_image",
        "EGL_KHR_gl_texture_cubemap_image",
        "EGL_KHR_gl_texture_3D_image",
        "EGL_KHR_gl_renderbuffer_image",
        "EGL_IMG_cl_image",
        "EGL_KHR_fence_sync",
        "EGL_KHR_wait_sync",
        "EGL_EXT_create_context_robustness",
        "EGL_IMG_image_plane_attribs",
        "EGL_EXT_swap_buffers_with_damage",
        "EGL_KHR_swap_buffers_with_damage",
        "EGL_KHR_partial_update",
        "EGL_EXT_buffer_age",
        "EGL_EXT_image_dma_buf_import",
        "EGL_EXT_image_dma_buf_import_modifiers",
        "EGL_EXT_yuv_surface",
        "EGL_IMG_context_priority",
        "EGL_KHR_create_context",
        "EGL_KHR_surfaceless_context",
        "EGL_KHR_no_config_context",
        "EGL_KHR_gl_colorspace",
        "EGL_EXT_image_gl_colorspace",
    ]
[1970-03-03T01:31:00Z DEBUG wgpu_hal::gles::egl]        EGL surface: +srgb khr
[1970-03-03T01:31:00Z DEBUG wgpu_hal::gles::egl]        Trying native-render
[1970-03-03T01:31:00Z DEBUG wgpu_hal::gles::egl] No config found!
[1970-03-03T01:31:00Z DEBUG wgpu_hal::gles::egl]        Trying presentation
[1970-03-03T01:31:00Z INFO  wgpu_hal::gles::egl] EGL says it can present to the window but not natively
[1970-03-03T01:31:00Z DEBUG wgpu_hal::gles::egl]        EGL context: +robust access EXT
[1970-03-03T01:31:00Z DEBUG wgpu_hal::gles::egl]        EGL context: +surfaceless
[1970-03-03T01:31:00Z DEBUG wgpu_core::instance] Instance::new: created Gl backend

thread 'main' (8255) panicked at /home/ubuntu/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/glow-0.16.0/src/gl46.rs:4515:5:
called glGetString but it was not loaded.
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

@ZengGengSen
Copy link
Author

ZengGengSen commented Jan 5, 2026

Add an external interface for the gl library, allowing users to provide the path of the gl library for direct reading.

diff --git a/wgpu-hal/src/gles/egl.rs b/wgpu-hal/src/gles/egl.rs
index 606d1b2d4..ee5055b0e 100644
--- a/wgpu-hal/src/gles/egl.rs
+++ b/wgpu-hal/src/gles/egl.rs
@@ -680,6 +680,8 @@ pub struct Instance {
     flags: wgt::InstanceFlags,
     options: wgt::GlBackendOptions,
     inner: Mutex<Inner>,
+    #[cfg(not(Emscripten))]
+    gl_library: Option<Arc<libloading::Library>>,
 }
 
 impl Instance {
@@ -885,6 +887,15 @@ impl crate::Instance for Instance {
             flags: desc.flags,
             options: desc.backend_options.gl.clone(),
             inner: Mutex::new(inner),
+            #[cfg(not(Emscripten))]
+            gl_library: desc
+                .backend_options
+                .gl
+                .gl_library
+                .as_ref()
+                .map_or(None, |path| {
+                    unsafe { libloading::Library::new(path) }.map(Arc::new).ok()
+                }),
         })
     }
 
@@ -964,14 +975,43 @@ impl crate::Instance for Instance {
         let inner = self.inner.lock();
         inner.egl.make_current();
 
-        let mut gl = unsafe {
-            glow::Context::from_loader_function(|name| {
+        let mut gl = {
+            let egl_load_func = |name: &str| {
                 inner
                     .egl
                     .instance
                     .get_proc_address(name)
                     .map_or(ptr::null(), |p| p as *const _)
-            })
+            };
+
+            #[cfg(not(Emscripten))]
+            {
+                if let Some(ref library) = self.gl_library {
+                    unsafe {
+                        glow::Context::from_loader_function(|name| {
+                            let symbol_name = std::ffi::CString::new(name).unwrap();
+                            let ptr = match library
+                                .get::<*const ffi::c_void>(symbol_name.as_bytes_with_nul())
+                            {
+                                Ok(symbol) => *symbol,
+                                Err(_) => ptr::null(),
+                            };
+                            if ptr.is_null() {
+                                egl_load_func(name)
+                            } else {
+                                ptr
+                            }
+                        })
+                    }
+                } else {
+                    unsafe { glow::Context::from_loader_function(egl_load_func) }
+                }
+            }
+
+            #[cfg(Emscripten)]
+            {
+                unsafe { glow::Context::from_loader_function(egl_load_func) }
+            }
         };
 
         // In contrast to OpenGL ES, OpenGL requires explicitly enabling sRGB conversions,
diff --git a/wgpu-types/src/backend.rs b/wgpu-types/src/backend.rs
index 5c6fa2980..20d7399ab 100644
--- a/wgpu-types/src/backend.rs
+++ b/wgpu-types/src/backend.rs
@@ -252,6 +252,8 @@ pub struct GlBackendOptions {
     pub gles_minor_version: Gles3MinorVersion,
     /// Behavior of OpenGL fences. Affects how `on_completed_work_done` and `device.poll` behave.
     pub fence_behavior: GlFenceBehavior,
+    /// The GL library path to load, if any.
+    pub gl_library: Option<String>,
 }
 
 impl GlBackendOptions {
@@ -264,6 +266,7 @@ impl GlBackendOptions {
         Self {
             gles_minor_version,
             fence_behavior: GlFenceBehavior::Normal,
+            gl_library: None,
         }
     }
 
@@ -277,6 +280,7 @@ impl GlBackendOptions {
         Self {
             gles_minor_version,
             fence_behavior: short_circuit_fences,
+            gl_library: None,
         }
     }
 }
diff --git a/wgpu-types/src/surface.rs b/wgpu-types/src/surface.rs
index 316135323..fc01a1801 100644
--- a/wgpu-types/src/surface.rs
+++ b/wgpu-types/src/surface.rs
@@ -105,7 +105,7 @@ pub enum PresentMode {
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
 pub enum CompositeAlphaMode {
-    /// Chooses either `Opaque` or `Inherit` automatically, depending on the
+    /// Chooses either `Opaque` or `Inherit` automatically,depending on the
     /// `alpha_mode` that the current surface can support.
     #[default]
     Auto = 0,
diff --git a/src/main.rs b/src/main.rs
index 6212408..af10d5e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -129,6 +129,7 @@ fn run_with_fbdev() -> Result<(), Box<dyn std::error::Error>> {
         // window: Some(Box::new(window.clone())),
         backend_options: wgpu::BackendOptions {
             gl: wgpu::GlBackendOptions {
+                gl_library: Some("libGLESv2.so.2".to_string()),
                 ..Default::default()
             },
             ..Default::default()

I found that the problem lies in the "choose_config" function.

let (config, supports_native_window) = choose_config(&egl, display, srgb_kind)?;

It return the supports_native_window is false

> adb shell /fdgpu

[1970-03-03T01:43:44Z DEBUG wgpu_hal::gles::egl] Client extensions: []
[1970-03-03T01:43:44Z DEBUG wgpu_hal::gles::egl] No (or unknown) windowing system (Some(Gbm(GbmDisplayHandle { gbm_device: 0x0 })), None) and EGL_MESA_platform_surfaceless not available. Using default platform
[1970-03-03T01:43:44Z DEBUG wgpu_hal::gles::egl] Display vendor "Imagination Technologies", version (1, 4)
[1970-03-03T01:43:44Z DEBUG wgpu_hal::gles::egl] Display extensions: [
        "EGL_KHR_image",
        "EGL_KHR_image_base",
        "EGL_KHR_gl_texture_2D_image",
        "EGL_KHR_gl_texture_cubemap_image",
        "EGL_KHR_gl_texture_3D_image",
        "EGL_KHR_gl_renderbuffer_image",
        "EGL_IMG_cl_image",
        "EGL_KHR_fence_sync",
        "EGL_KHR_wait_sync",
        "EGL_EXT_create_context_robustness",
        "EGL_IMG_image_plane_attribs",
        "EGL_EXT_swap_buffers_with_damage",
        "EGL_KHR_swap_buffers_with_damage",
        "EGL_KHR_partial_update",
        "EGL_EXT_buffer_age",
        "EGL_EXT_image_dma_buf_import",
        "EGL_EXT_image_dma_buf_import_modifiers",
        "EGL_EXT_yuv_surface",
        "EGL_IMG_context_priority",
        "EGL_KHR_create_context",
        "EGL_KHR_surfaceless_context",
        "EGL_KHR_no_config_context",
        "EGL_KHR_gl_colorspace",
        "EGL_EXT_image_gl_colorspace",
    ]
[1970-03-03T01:43:44Z DEBUG wgpu_hal::gles::egl]        EGL surface: +srgb khr
[1970-03-03T01:43:44Z DEBUG wgpu_hal::gles::egl]        Trying native-render
[1970-03-03T01:43:44Z DEBUG wgpu_hal::gles::egl] No config found!
[1970-03-03T01:43:44Z DEBUG wgpu_hal::gles::egl]        Trying presentation
[1970-03-03T01:43:44Z INFO  wgpu_hal::gles::egl] EGL says it can present to the window but not natively
[1970-03-03T01:43:44Z DEBUG wgpu_hal::gles::egl]        EGL context: +robust access EXT
[1970-03-03T01:43:44Z DEBUG wgpu_hal::gles::egl]        EGL context: +surfaceless
[1970-03-03T01:43:44Z DEBUG wgpu_core::instance] Instance::new: created Gl backend
[1970-03-03T01:43:44Z DEBUG wgpu_hal::gles::adapter] Vendor: Imagination Technologies
[1970-03-03T01:43:44Z DEBUG wgpu_hal::gles::adapter] Renderer: PowerVR Rogue GE8300
[1970-03-03T01:43:44Z DEBUG wgpu_hal::gles::adapter] Version: OpenGL ES 3.2 build 1.19@6345021
[1970-03-03T01:43:44Z DEBUG wgpu_hal::gles::adapter] SL version: OpenGL ES GLSL ES 3.20 build 1.19@6345021
[1970-03-03T01:43:44Z DEBUG wgpu_hal::gles::adapter] Supported GL Extensions: {
        "GL_EXT_texture_format_BGRA8888",
        "GL_OES_rgb8_rgba8",
        "GL_OES_sample_variables",
        "GL_OES_texture_half_float",
        "GL_OES_texture_npot",
        "GL_EXT_shader_framebuffer_fetch",
        "GL_EXT_sRGB_write_control",
        "GL_EXT_compressed_ETC1_RGB8_sub_texture",
        "GL_EXT_memory_object_fd",
        "GL_EXT_polygon_offset_clamp",
        "GL_ANDROID_extension_pack_es31a",
        "GL_EXT_tessellation_shader",
        "GL_EXT_texture_buffer",
        "GL_IMG_shader_binary",
        "GL_EXT_texture_rg",
        "GL_EXT_geometry_shader",
        "GL_KHR_blend_equation_advanced",
        "GL_EXT_robustness",
        "GL_OES_depth_texture",
        "GL_EXT_occlusion_query_boolean",
        "GL_EXT_color_buffer_half_float",
        "GL_EXT_conservative_depth",
        "GL_APPLE_texture_format_BGRA8888",
        "GL_EXT_draw_buffers_indexed",
        "GL_EXT_separate_shader_objects",
        "GL_EXT_shader_io_blocks",
        "GL_EXT_tessellation_point_size",
        "GL_EXT_draw_elements_base_vertex",
        "GL_EXT_memory_object",
        "GL_IMG_multisampled_render_to_texture",
        "GL_IMG_texture_compression_pvrtc2",
        "GL_KHR_texture_compression_astc_ldr",
        "GL_EXT_texture_sRGB_R8",
        "GL_EXT_shader_pixel_local_storage2",
        "GL_OES_compressed_ETC1_RGB8_texture",
        "GL_EXT_pvrtc_sRGB",
        "GL_OES_EGL_image",
        "GL_EXT_float_blend",
        "GL_IMG_program_binary",
        "GL_OES_draw_elements_base_vertex",
        "GL_IMG_framebuffer_downsample",
        "GL_EXT_EGL_image_array",
        "GL_EXT_discard_framebuffer",
        "GL_OES_get_program_binary",
        "GL_IMG_texture_compression_pvrtc",
        "GL_OES_sample_shading",
        "GL_OES_shader_image_atomic",
        "GL_IMG_texture_npot",
        "GL_OES_shader_io_blocks",
        "GL_EXT_buffer_storage",
        "GL_OES_texture_3D",
        "GL_OES_texture_buffer",
        "GL_OES_texture_cube_map_array",
        "GL_OES_texture_border_clamp",
        "GL_EXT_texture_format_sRGB_override",
        "GL_EXT_texture_sRGB_decode",
        "GL_EXT_texture_border_clamp",
        "GL_EXT_multisampled_render_to_texture",
        "GL_EXT_gpu_shader5",
        "GL_EXT_read_format_bgra",
        "GL_EXT_shader_group_vote",
        "GL_EXT_shader_implicit_conversions",
        "GL_EXT_YUV_target",
        "GL_EXT_texture_sRGB_RG8",
        "GL_OES_geometry_point_size",
        "GL_EXT_texture_cube_map_array",
        "GL_OES_geometry_shader",
        "GL_OES_surfaceless_context",
        "GL_OES_tessellation_point_size",
        "GL_OES_tessellation_shader",
        "GL_OES_texture_float",
        "GL_EXT_color_buffer_float",
        "GL_OES_texture_stencil8",
        "GL_EXT_sparse_texture",
        "GL_EXT_sRGB",
        "GL_EXT_shader_texture_lod",
        "GL_EXT_shadow_samplers",
        "GL_EXT_copy_image",
        "GL_OES_required_internalformat",
        "GL_OES_vertex_array_object",
        "GL_EXT_clear_texture",
        "GL_EXT_draw_buffers",
        "GL_EXT_multisampled_render_to_texture2",
        "GL_EXT_clip_control",
        "GL_EXT_shader_non_constant_global_initializers",
        "GL_OES_depth_texture_cube_map",
        "GL_OES_depth24",
        "GL_OES_mapbuffer",
        "GL_EXT_multi_draw_arrays",
        "GL_OES_fragment_precision_high",
        "GL_OES_packed_depth_stencil",
        "GL_EXT_unpack_subimage",
        "GL_OES_standard_derivatives",
        "GL_OES_vertex_half_float",
        "GL_KHR_blend_equation_advanced_coherent",
        "GL_EXT_shader_pixel_local_storage",
        "GL_IMG_read_format",
        "GL_OES_element_index_uint",
        "GL_EXT_texture_shadow_lod",
        "GL_EXT_geometry_point_size",
        "GL_EXT_texture_type_2_10_10_10_REV",
        "GL_OES_texture_storage_multisample_2d_array",
        "GL_OES_draw_buffers_indexed",
        "GL_IMG_texture_format_BGRA8888",
        "GL_OES_shader_multisample_interpolation",
        "GL_EXT_blend_minmax",
        "GL_EXT_primitive_bounding_box",
        "GL_KHR_debug",
        "GL_KHR_robustness",
        "GL_OES_EGL_sync",
        "GL_OES_gpu_shader5",
    }
[1970-03-03T01:43:44Z DEBUG wgpu_core::instance] Found 1 compatible adapters. Sorted by preference:
[1970-03-03T01:43:44Z DEBUG wgpu_core::instance] * AdapterInfo { name: "PowerVR Rogue GE8300", vendor: 0, device: 0, device_type: Other, device_pci_bus_id: "", driver: "", driver_info: "OpenGL ES 3.2 build 1.19@6345021", backend: Gl, subgroup_min_size: 4, subgroup_max_size: 128, transient_saves_memory: false }
[1970-03-03T01:43:44Z DEBUG wgpu_core::instance] Request adapter result AdapterInfo { name: "PowerVR Rogue GE8300", vendor: 0, device: 0, device_type: Other, device_pci_bus_id: "", driver: "", driver_info: "OpenGL ES 3.2 build 1.19@6345021", backend: Gl, subgroup_min_size: 4, subgroup_max_size: 128, transient_saves_memory: false }
[1970-03-03T01:43:44Z DEBUG wgpu_hal::gles::device] Naga generated shader:
    #version 320 es

    precision highp float;
    precision highp int;

    layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;

    struct OffsetPc {
        uint inner;
    };
    layout(std430, binding = 0) buffer type_1_block_0Compute { uint _group_0_binding_0_cs[6]; };

    layout(std430, binding = 1) readonly buffer type_2_block_1Compute { uint _group_1_binding_0_cs[]; };

    uniform OffsetPc _immediates_binding_cs;


    void main() {
        bool local = false;
        bool local_1 = false;
        uint _e3 = _immediates_binding_cs.inner;
        uint _e5 = _group_1_binding_0_cs[_e3];
        uint _e9 = _immediates_binding_cs.inner;
        uint _e13 = _group_1_binding_0_cs[(_e9 + 1u)];
        uint _e17 = _immediates_binding_cs.inner;
        uint _e21 = _group_1_binding_0_cs[(_e17 + 2u)];
        uvec3 src_1 = uvec3(_e5, _e13, _e21);
        if (!((src_1.x > 65535u))) {
            local = (src_1.y > 65535u);
        } else {
            local = true;
        }
        bool _e32 = local;
        if (!(_e32)) {
            local_1 = (src_1.z > 65535u);
        } else {
            local_1 = true;
        }
        bool _e39 = local_1;
        if (_e39) {
            _group_0_binding_0_cs = uint[6](0u, 0u, 0u, 0u, 0u, 0u);
            return;
        } else {
            _group_0_binding_0_cs = uint[6](src_1.x, src_1.y, src_1.z, src_1.x, src_1.y, src_1.z);
            return;
        }
    }


[1970-03-03T01:43:44Z DEBUG wgpu_hal::gles::device]     Compiled shader NativeShader(2)
[1970-03-03T01:43:44Z DEBUG wgpu_hal::gles::device]     Linked program NativeProgram(1)
[1970-03-03T01:43:44Z DEBUG wgpu_hal::gles::device] Naga generated shader:
    #version 320 es

    precision highp float;
    precision highp int;

    layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;

    struct MetadataEntry {
        uint src_offset;
        uint dst_offset;
        uint vertex_or_index_limit;
        uint instance_limit;
    };
    struct MetadataRange {
        uint start;
        uint count;
    };
    const bool supports_indirect_first_instance = false;
    const bool write_d3d12_special_constants = false;

    uniform MetadataRange _immediates_binding_cs;

    layout(std430, binding = 0) readonly buffer type_2_block_0Compute { MetadataEntry _group_0_binding_0_cs[]; };

    layout(std430, binding = 1) readonly buffer type_3_block_1Compute { uint _group_1_binding_0_cs[]; };

    layout(std430, binding = 2) buffer type_3_block_2Compute { uint _group_2_binding_0_cs[]; };


    bool is_bit_set(uint data, uint index) {
        return (((data >> index) & 1u) == 1u);
    }

    void main() {
        uvec3 global_invocation_id = gl_GlobalInvocationID;
        bool failed = false;
        bool local = false;
        bool local_1 = false;
        uint _e4 = _immediates_binding_cs.count;
        if ((global_invocation_id.x >= _e4)) {
            return;
        }
        uint _e9 = _immediates_binding_cs.start;
        MetadataEntry metadata_1 = _group_0_binding_0_cs[(_e9 + global_invocation_id.x)];
        bool _e18 = is_bit_set(metadata_1.src_offset, 31u);
        uint src_base_offset = ((metadata_1.src_offset << 2u) >> 2u);
        uint dst_base_offset = ((metadata_1.dst_offset << 2u) >> 2u);
        uint first_vertex_or_index = _group_1_binding_0_cs[(src_base_offset + 2u)];
        uint vertex_or_index_count = _group_1_binding_0_cs[(src_base_offset + 0u)];
        {
            bool _e41 = is_bit_set(metadata_1.dst_offset, 30u);
            bool sub_overflows = (metadata_1.vertex_or_index_limit < first_vertex_or_index);
            if (sub_overflows) {
                local = !(_e41);
            } else {
                local = false;
            }
            bool _e48 = local;
            bool _e49 = failed;
            failed = (_e49 || _e48);
            uint vertex_or_index_limit = (metadata_1.vertex_or_index_limit - first_vertex_or_index);
            bool _e54 = failed;
            failed = (_e54 || (vertex_or_index_limit < vertex_or_index_count));
        }
        uint first_instance = _group_1_binding_0_cs[((src_base_offset + 3u) + uint(_e18))];
        uint instance_count = _group_1_binding_0_cs[(src_base_offset + 1u)];
        {
            bool _e70 = is_bit_set(metadata_1.dst_offset, 31u);
            bool sub_overflows_1 = (metadata_1.instance_limit < first_instance);
            if (sub_overflows_1) {
                local_1 = !(_e70);
            } else {
                local_1 = false;
            }
            bool _e77 = local_1;
            bool _e78 = failed;
            failed = (_e78 || _e77);
            uint instance_limit = (metadata_1.instance_limit - first_instance);
            bool _e83 = failed;
            failed = (_e83 || (instance_limit < instance_count));
        }
        if (true) {
            bool _e90 = failed;
            failed = (_e90 || (first_instance != 0u));
        }
        bool _e97 = failed;
        if (_e97) {
            if (write_d3d12_special_constants) {
                _group_2_binding_0_cs[(dst_base_offset + 0u)] = 0u;
                _group_2_binding_0_cs[(dst_base_offset + 1u)] = 0u;
                _group_2_binding_0_cs[(dst_base_offset + 2u)] = 0u;
            }
            _group_2_binding_0_cs[((dst_base_offset + 0u) + 0u)] = 0u;
            _group_2_binding_0_cs[((dst_base_offset + 0u) + 1u)] = 0u;
            _group_2_binding_0_cs[((dst_base_offset + 0u) + 2u)] = 0u;
            _group_2_binding_0_cs[((dst_base_offset + 0u) + 3u)] = 0u;
            if (_e18) {
                _group_2_binding_0_cs[((dst_base_offset + 0u) + 4u)] = 0u;
                return;
            } else {
                return;
            }
        } else {
            if (write_d3d12_special_constants) {
                uint _e155 = _group_1_binding_0_cs[((src_base_offset + 2u) + uint(_e18))];
                _group_2_binding_0_cs[(dst_base_offset + 0u)] = _e155;
                uint _e166 = _group_1_binding_0_cs[((src_base_offset + 3u) + uint(_e18))];
                _group_2_binding_0_cs[(dst_base_offset + 1u)] = _e166;
                _group_2_binding_0_cs[(dst_base_offset + 2u)] = 0u;
            }
            uint _e181 = _group_1_binding_0_cs[(src_base_offset + 0u)];
            _group_2_binding_0_cs[((dst_base_offset + 0u) + 0u)] = _e181;
            uint _e191 = _group_1_binding_0_cs[(src_base_offset + 1u)];
            _group_2_binding_0_cs[((dst_base_offset + 0u) + 1u)] = _e191;
            uint _e201 = _group_1_binding_0_cs[(src_base_offset + 2u)];
            _group_2_binding_0_cs[((dst_base_offset + 0u) + 2u)] = _e201;
            uint _e211 = _group_1_binding_0_cs[(src_base_offset + 3u)];
            _group_2_binding_0_cs[((dst_base_offset + 0u) + 3u)] = _e211;
            if (_e18) {
                uint _e221 = _group_1_binding_0_cs[(src_base_offset + 4u)];
                _group_2_binding_0_cs[((dst_base_offset + 0u) + 4u)] = _e221;
                return;
            } else {
                return;
            }
        }
    }


[1970-03-03T01:43:44Z DEBUG wgpu_hal::gles::device]     Compiled shader NativeShader(4)
[1970-03-03T01:43:44Z DEBUG wgpu_hal::gles::device]     Linked program NativeProgram(3)

thread 'main' (8374) panicked at src/renderer.rs:51:33:
index out of bounds: the len is 0 but the index is 0
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

@ZengGengSen
Copy link
Author

Finally, after modifying the "choose_config" part, it was possible to run it.

diff --git a/wgpu-hal/src/gles/egl.rs b/wgpu-hal/src/gles/egl.rs
index d7546d619..975a3939e 100644
--- a/wgpu-hal/src/gles/egl.rs
+++ b/wgpu-hal/src/gles/egl.rs
@@ -88,82 +88,217 @@ enum SrgbFrameBufferKind {
     Khr,
 }
 
+struct EglConfig {
+    red_size: khronos_egl::Int,
+    green_size: khronos_egl::Int,
+    blue_size: khronos_egl::Int,
+    alpha_size: khronos_egl::Int,
+    depth_size: khronos_egl::Int,
+    buffer_size: khronos_egl::Int,
+    stencil_size: khronos_egl::Int,
+    multi_sample_buffers: khronos_egl::Int,
+    multi_sample_samples: khronos_egl::Int,
+    floating_point: bool,
+    offscreen: bool,
+    profile: khronos_egl::Int,
+    surface_type: khronos_egl::Int,
+    native_renderable: bool,
+}
+
+impl Default for EglConfig {
+    fn default() -> Self {
+        Self {
+            red_size: 8,
+            green_size: 8,
+            blue_size: 8,
+            alpha_size: 8,
+            depth_size: 16,
+            buffer_size: 0,
+            stencil_size: 0,
+            multi_sample_buffers: 0,
+            multi_sample_samples: 0,
+            floating_point: false,
+            offscreen: false,
+            profile: khronos_egl::OPENGL_BIT,
+            surface_type: 0,
+            native_renderable: false,
+        }
+    }
+}
+
 /// Choose GLES framebuffer configuration.
-fn choose_config(
+fn choose_config_inner(
     egl: &EglInstance,
     display: khronos_egl::Display,
-    srgb_kind: SrgbFrameBufferKind,
+    egl_config: &EglConfig,
+    set_config_caveat_none: bool,
 ) -> Result<(khronos_egl::Config, bool), crate::InstanceError> {
-    //TODO: EGL_SLOW_CONFIG
-    let tiers = [
-        (
-            "off-screen",
-            &[
-                khronos_egl::SURFACE_TYPE,
-                khronos_egl::PBUFFER_BIT,
-                khronos_egl::RENDERABLE_TYPE,
-                khronos_egl::OPENGL_ES2_BIT,
-            ][..],
-        ),
-        (
-            "presentation",
-            &[khronos_egl::SURFACE_TYPE, khronos_egl::WINDOW_BIT][..],
-        ),
-        #[cfg(not(target_os = "android"))]
-        (
-            "native-render",
-            &[khronos_egl::NATIVE_RENDERABLE, khronos_egl::TRUE as _][..],
-        ),
-    ];
-
-    let mut attributes = Vec::with_capacity(9);
-    for tier_max in (0..tiers.len()).rev() {
-        let name = tiers[tier_max].0;
-        log::debug!("\tTrying {name}");
-
-        attributes.clear();
-        for &(_, tier_attr) in tiers[..=tier_max].iter() {
-            attributes.extend_from_slice(tier_attr);
-        }
-        // make sure the Alpha is enough to support sRGB
-        match srgb_kind {
-            SrgbFrameBufferKind::None => {}
-            _ => {
-                attributes.push(khronos_egl::ALPHA_SIZE);
-                attributes.push(8);
-            }
-        }
+    let mut attributes = Vec::with_capacity(32);
+
+    attributes.push(khronos_egl::RED_SIZE);
+    attributes.push(egl_config.red_size);
+    attributes.push(khronos_egl::GREEN_SIZE);
+    attributes.push(egl_config.green_size);
+    attributes.push(khronos_egl::BLUE_SIZE);
+    attributes.push(egl_config.blue_size);
+
+    if set_config_caveat_none {
+        attributes.push(khronos_egl::CONFIG_CAVEAT);
         attributes.push(khronos_egl::NONE);
+    }
 
-        match egl.choose_first_config(display, &attributes) {
-            Ok(Some(config)) => {
-                if tier_max == 1 {
-                    //Note: this has been confirmed to malfunction on Intel+NV laptops,
-                    // but also on Angle.
-                    log::info!("EGL says it can present to the window but not natively",);
-                }
-                // Android emulator can't natively present either.
-                let tier_threshold =
-                    if cfg!(target_os = "android") || cfg!(windows) || cfg!(target_env = "ohos") {
-                        1
-                    } else {
-                        2
-                    };
-                return Ok((config, tier_max >= tier_threshold));
-            }
-            Ok(None) => {
-                log::debug!("No config found!");
-            }
-            Err(e) => {
-                log::error!("error in choose_first_config: {e:?}");
+    if egl_config.alpha_size > 0 {
+        attributes.push(khronos_egl::ALPHA_SIZE);
+        attributes.push(egl_config.alpha_size);
+    }
+
+    if egl_config.depth_size > 0 {
+        attributes.push(khronos_egl::DEPTH_SIZE);
+        attributes.push(egl_config.depth_size);
+    }
+
+    if egl_config.stencil_size > 0 {
+        attributes.push(khronos_egl::STENCIL_SIZE);
+        attributes.push(egl_config.stencil_size);
+    }
+
+    if egl_config.buffer_size > 0 {
+        attributes.push(khronos_egl::BUFFER_SIZE);
+        attributes.push(egl_config.buffer_size);
+    }
+
+    if egl_config.multi_sample_buffers > 0 {
+        attributes.push(khronos_egl::SAMPLE_BUFFERS);
+        attributes.push(egl_config.multi_sample_buffers);
+    }
+
+    if egl_config.multi_sample_samples > 0 {
+        attributes.push(khronos_egl::SAMPLES);
+        attributes.push(egl_config.multi_sample_samples);
+    }
+
+    if egl_config.floating_point {
+        // TODO: EGL_EXT_pixel_format_float Support?
+        // attributes.push(khronos_egl::COLOR_COMPNENT_TYPE_EXT);
+        // attributes.push(khronos_egl::COLOR_COMPONENT_TYPE_FLOAT_EXT);
+    }
+
+    if egl_config.offscreen {
+        attributes.push(khronos_egl::SURFACE_TYPE);
+        attributes.push(khronos_egl::PBUFFER_BIT);
+    }
+
+    attributes.push(khronos_egl::RENDERABLE_TYPE);
+    attributes.push(egl_config.profile);
+
+    if egl_config.surface_type != 0 {
+        attributes.push(khronos_egl::SURFACE_TYPE);
+        attributes.push(egl_config.surface_type);
+    }
+
+    if egl_config.native_renderable {
+        attributes.push(khronos_egl::NATIVE_RENDERABLE);
+        attributes.push(khronos_egl::TRUE as _);
+    }
+
+    attributes.push(khronos_egl::NONE);
+
+    let mut configs = Vec::with_capacity(128);
+    egl.choose_config(display, &attributes, &mut configs)
+        .map_err(|e| {
+            crate::InstanceError::with_source(String::from("error in eglChooseConfig"), e)
+        })?;
+
+    if configs.is_empty() {
+        return Err(crate::InstanceError::new(String::from(
+            "no matching EGL framebuffer configuration found",
+        )));
+    }
+
+    // eglChooseConfig returns a number of configurations that match or exceed the requested attribs.
+    // From those, we select the one that matches our requirements more closely via a makeshift algorithm
+    let mut best_bitdiff = None;
+    let mut best_config = None;
+    for config in configs.iter() {
+        let mut bitdiff = 0;
+
+        let red_size = egl
+            .get_config_attrib(display, *config, khronos_egl::RED_SIZE)
+            .unwrap_or(0);
+        let green_size = egl
+            .get_config_attrib(display, *config, khronos_egl::GREEN_SIZE)
+            .unwrap_or(0);
+        let blue_size = egl
+            .get_config_attrib(display, *config, khronos_egl::BLUE_SIZE)
+            .unwrap_or(0);
+        let alpha_size = egl
+            .get_config_attrib(display, *config, khronos_egl::ALPHA_SIZE)
+            .unwrap_or(0);
+
+        bitdiff += (red_size - egl_config.red_size).abs();
+        bitdiff += (green_size - egl_config.green_size).abs();
+        bitdiff += (blue_size - egl_config.blue_size).abs();
+        bitdiff += (alpha_size - egl_config.alpha_size).abs();
+
+        if let Some(diff) = best_bitdiff {
+            if bitdiff < diff {
+                best_bitdiff = Some(bitdiff);
+                best_config = Some(*config);
             }
+        } else {
+            best_bitdiff = Some(bitdiff);
+            best_config = Some(*config);
         }
     }
 
-    // TODO: include diagnostic details that are currently logged
-    Err(crate::InstanceError::new(String::from(
-        "unable to find an acceptable EGL framebuffer configuration",
-    )))
+    if let Some(config) = best_config {
+        let surface_type = egl
+            .get_config_attrib(display, config, khronos_egl::SURFACE_TYPE)
+            .unwrap_or(0);
+
+        let support_window_bit = (surface_type & khronos_egl::WINDOW_BIT) != 0;
+
+        let supports_native_window =
+            if cfg!(target_os = "android") || cfg!(windows) || cfg!(target_env = "ohos") {
+                support_window_bit
+            } else {
+                let native_renderable = egl
+                    .get_config_attrib(display, config, khronos_egl::NATIVE_RENDERABLE)
+                    .unwrap_or(0);
+                let supports_native_renderable = native_renderable == khronos_egl::TRUE as i32;
+                support_window_bit && supports_native_renderable
+            };
+
+        Ok((config, supports_native_window))
+    } else {
+        Err(crate::InstanceError::new(String::from(
+            "no matching EGL framebuffer configuration found",
+        )))
+    }
+}
+
+fn choose_config(
+    egl: &EglInstance,
+    display: khronos_egl::Display,
+    egl_config: &EglConfig,
+) -> Result<(khronos_egl::Config, bool), crate::InstanceError> {
+    // Try with EGL_CONFIG_CAVEAT set to EGL_NONE, to avoid any EGL_SLOW_CONFIG or EGL_NON_CONFORMANT_CONFIG
+    match choose_config_inner(egl, display, egl_config, true) {
+        Ok(result) => return Ok(result),
+        Err(e) => {
+            log::info!(
+                "Failed to choose EGL config with CONFIG_CAVEAT=NONE, retrying without it: {e}"
+            );
+        }
+    }
+
+    match choose_config_inner(egl, display, egl_config, false) {
+        Ok(result) => Ok(result),
+        Err(e) => {
+            log::info!("Failed to choose EGL config without CONFIG_CAVEAT=NONE: {e}");
+            Err(e)
+        }
+    }
 }
 
 #[derive(Clone, Debug)]
@@ -441,8 +576,6 @@ impl Inner {
             }
         }
 
-        let (config, supports_native_window) = choose_config(&egl, display, srgb_kind)?;
-
         let supports_opengl = if version >= (1, 4) {
             let client_apis = egl
                 .query_string(Some(display), khronos_egl::CLIENT_APIS)
@@ -454,6 +587,27 @@ impl Inner {
         } else {
             false
         };
+
+        let profile = if supports_opengl {
+            khronos_egl::OPENGL_BIT
+        } else {
+            // TODO: Add OpenGL ES 3.x bit when supported
+            // if display_extensions.contains("EGL_KHR_create_context") {
+            //  khronos_egl::OPENGL_ES3_BIT
+            // }
+            khronos_egl::OPENGL_ES2_BIT
+        };
+
+        let egl_config = EglConfig {
+            profile,
+            alpha_size: match srgb_kind {
+                SrgbFrameBufferKind::None => 0,
+                _ => 8,
+            },
+            ..Default::default()
+        };
+        let (config, supports_native_window) = choose_config(&egl, display, &egl_config)?;
+
         egl.bind_api(if supports_opengl {
             khronos_egl::OPENGL_API
         } else {

@ZengGengSen
Copy link
Author

Also, can I ask you. For what tasks (if any) are you using LLMs or AI like ChatGPT? Are these errors discovered by such a tool? Is the code written by such a tool? Are you using one for translating?

The tools I use most frequently are Gemini and Copilot. Copilot is directly integrated into the IDE, while Gemini is used to help me analyze the output of some log issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants