diff options
Diffstat (limited to 'third_party/rust/wgpu-hal/src/gles')
-rw-r--r-- | third_party/rust/wgpu-hal/src/gles/adapter.rs | 73 | ||||
-rw-r--r-- | third_party/rust/wgpu-hal/src/gles/command.rs | 7 | ||||
-rw-r--r-- | third_party/rust/wgpu-hal/src/gles/device.rs | 5 | ||||
-rw-r--r-- | third_party/rust/wgpu-hal/src/gles/egl.rs | 69 | ||||
-rw-r--r-- | third_party/rust/wgpu-hal/src/gles/mod.rs | 53 | ||||
-rw-r--r-- | third_party/rust/wgpu-hal/src/gles/queue.rs | 8 | ||||
-rw-r--r-- | third_party/rust/wgpu-hal/src/gles/wgl.rs | 3 |
7 files changed, 179 insertions, 39 deletions
diff --git a/third_party/rust/wgpu-hal/src/gles/adapter.rs b/third_party/rust/wgpu-hal/src/gles/adapter.rs index afa4023797..c09725e85f 100644 --- a/third_party/rust/wgpu-hal/src/gles/adapter.rs +++ b/third_party/rust/wgpu-hal/src/gles/adapter.rs @@ -4,6 +4,7 @@ use std::sync::{atomic::AtomicU8, Arc}; use wgt::AstcChannel; use crate::auxil::db; +use crate::gles::ShaderClearProgram; // https://webgl2fundamentals.org/webgl/lessons/webgl-data-textures.html @@ -435,7 +436,8 @@ impl super::Adapter { let mut features = wgt::Features::empty() | wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES | wgt::Features::CLEAR_TEXTURE - | wgt::Features::PUSH_CONSTANTS; + | wgt::Features::PUSH_CONSTANTS + | wgt::Features::DEPTH32FLOAT_STENCIL8; features.set( wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER | wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO, extensions.contains("GL_EXT_texture_border_clamp") @@ -472,6 +474,7 @@ impl super::Adapter { features.set(wgt::Features::SHADER_UNUSED_VERTEX_OUTPUT, true); if extensions.contains("GL_ARB_timer_query") { features.set(wgt::Features::TIMESTAMP_QUERY, true); + features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS, true); features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_PASSES, true); } let gl_bcn_exts = [ @@ -652,6 +655,15 @@ impl super::Adapter { 0 }; + let max_color_attachments = unsafe { + gl.get_parameter_i32(glow::MAX_COLOR_ATTACHMENTS) + .min(gl.get_parameter_i32(glow::MAX_DRAW_BUFFERS)) + .min(crate::MAX_COLOR_ATTACHMENTS as i32) as u32 + }; + + // TODO: programmatically determine this. + let max_color_attachment_bytes_per_sample = 32; + let limits = wgt::Limits { max_texture_dimension_1d: max_texture_size, max_texture_dimension_2d: max_texture_size, @@ -719,9 +731,21 @@ impl super::Adapter { max_push_constant_size: super::MAX_PUSH_CONSTANTS as u32 * 4, min_uniform_buffer_offset_alignment, min_storage_buffer_offset_alignment, - max_inter_stage_shader_components: unsafe { - gl.get_parameter_i32(glow::MAX_VARYING_COMPONENTS) - } as u32, + max_inter_stage_shader_components: { + // MAX_VARYING_COMPONENTS may return 0, because it is deprecated since OpenGL 3.2 core, + // and an OpenGL Context with the core profile and with forward-compatibility=true, + // will make deprecated constants unavailable. + let max_varying_components = + unsafe { gl.get_parameter_i32(glow::MAX_VARYING_COMPONENTS) } as u32; + if max_varying_components == 0 { + // default value for max_inter_stage_shader_components + 60 + } else { + max_varying_components + } + }, + max_color_attachments, + max_color_attachment_bytes_per_sample, max_compute_workgroup_storage_size: if supports_work_group_params { (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHARED_MEMORY_SIZE) } as u32) } else { @@ -779,6 +803,7 @@ impl super::Adapter { } let downlevel_defaults = wgt::DownlevelLimits {}; + let max_samples = unsafe { gl.get_parameter_i32(glow::MAX_SAMPLES) }; // Drop the GL guard so we can move the context into AdapterShared // ( on Wasm the gl handle is just a ref so we tell clippy to allow @@ -797,6 +822,7 @@ impl super::Adapter { next_shader_id: Default::default(), program_cache: Default::default(), es: es_ver.is_some(), + max_msaa_samples: max_samples, }), }, info: Self::make_info(vendor, renderer), @@ -825,7 +851,14 @@ impl super::Adapter { let source = if es { format!("#version 300 es\nprecision lowp float;\n{source}") } else { - format!("#version 130\n{source}") + let version = gl.version(); + if version.major == 3 && version.minor == 0 { + // OpenGL 3.0 only supports this format + format!("#version 130\n{source}") + } else { + // OpenGL 3.1+ support this format + format!("#version 140\n{source}") + } }; let shader = unsafe { gl.create_shader(shader_type) }.expect("Could not create shader"); unsafe { gl.shader_source(shader, &source) }; @@ -846,7 +879,7 @@ impl super::Adapter { unsafe fn create_shader_clear_program( gl: &glow::Context, es: bool, - ) -> Option<(glow::Program, glow::UniformLocation)> { + ) -> Option<ShaderClearProgram> { let program = unsafe { gl.create_program() }.expect("Could not create shader program"); let vertex = unsafe { Self::compile_shader( @@ -882,7 +915,10 @@ impl super::Adapter { unsafe { gl.delete_shader(vertex) }; unsafe { gl.delete_shader(fragment) }; - Some((program, color_uniform_location)) + Some(ShaderClearProgram { + program, + color_uniform_location, + }) } } @@ -908,9 +944,18 @@ impl crate::Adapter<super::Api> for super::Adapter { // Compile the shader program we use for doing manual clears to work around Mesa fastclear // bug. - let (shader_clear_program, shader_clear_program_color_uniform_location) = unsafe { - Self::create_shader_clear_program(gl, self.shared.es) - .ok_or(crate::DeviceError::ResourceCreationFailed)? + let shader_clear_program = if self + .shared + .workarounds + .contains(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR) + { + Some(unsafe { + Self::create_shader_clear_program(gl, self.shared.es) + .ok_or(crate::DeviceError::ResourceCreationFailed)? + }) + } else { + // If we don't need the workaround, don't waste time and resources compiling the clear program + None }; Ok(crate::OpenDevice { @@ -928,7 +973,6 @@ impl crate::Adapter<super::Api> for super::Adapter { copy_fbo: unsafe { gl.create_framebuffer() } .map_err(|_| crate::DeviceError::OutOfMemory)?, shader_clear_program, - shader_clear_program_color_uniform_location, zero_buffer, temp_query_results: Mutex::new(Vec::new()), draw_buffer_count: AtomicU8::new(1), @@ -945,12 +989,7 @@ impl crate::Adapter<super::Api> for super::Adapter { use wgt::TextureFormat as Tf; let sample_count = { - let max_samples = unsafe { - self.shared - .context - .lock() - .get_parameter_i32(glow::MAX_SAMPLES) - }; + let max_samples = self.shared.max_msaa_samples; if max_samples >= 16 { Tfc::MULTISAMPLE_X2 | Tfc::MULTISAMPLE_X4 diff --git a/third_party/rust/wgpu-hal/src/gles/command.rs b/third_party/rust/wgpu-hal/src/gles/command.rs index 926122e4ad..4385e2a31e 100644 --- a/third_party/rust/wgpu-hal/src/gles/command.rs +++ b/third_party/rust/wgpu-hal/src/gles/command.rs @@ -93,6 +93,13 @@ impl super::CommandBuffer { } } +impl Drop for super::CommandEncoder { + fn drop(&mut self) { + use crate::CommandEncoder; + unsafe { self.discard_encoding() } + } +} + impl super::CommandEncoder { fn rebind_stencil_func(&mut self) { fn make(s: &super::StencilSide, face: u32) -> C { diff --git a/third_party/rust/wgpu-hal/src/gles/device.rs b/third_party/rust/wgpu-hal/src/gles/device.rs index d0abe2c169..2678488cf8 100644 --- a/third_party/rust/wgpu-hal/src/gles/device.rs +++ b/third_party/rust/wgpu-hal/src/gles/device.rs @@ -1194,13 +1194,16 @@ impl crate::Device<super::Api> for super::Device { let sampler = desc.samplers[entry.resource_index as usize]; super::RawBinding::Sampler(sampler.raw) } - wgt::BindingType::Texture { .. } => { + wgt::BindingType::Texture { view_dimension, .. } => { let view = desc.textures[entry.resource_index as usize].view; if view.array_layers.start != 0 { log::error!("Unable to create a sampled texture binding for non-zero array layer.\n{}", "This is an implementation problem of wgpu-hal/gles backend.") } let (raw, target) = view.inner.as_native(); + + super::Texture::log_failing_target_heuristics(view_dimension, target); + super::RawBinding::Texture { raw, target, diff --git a/third_party/rust/wgpu-hal/src/gles/egl.rs b/third_party/rust/wgpu-hal/src/gles/egl.rs index aa985d8121..f4bfcf5487 100644 --- a/third_party/rust/wgpu-hal/src/gles/egl.rs +++ b/third_party/rust/wgpu-hal/src/gles/egl.rs @@ -1,7 +1,8 @@ use glow::HasContext; +use once_cell::sync::Lazy; use parking_lot::{Mutex, MutexGuard, RwLock}; -use std::{ffi, os::raw, ptr, rc::Rc, sync::Arc, time::Duration}; +use std::{collections::HashMap, ffi, os::raw, ptr, rc::Rc, sync::Arc, time::Duration}; /// The amount of time to wait while trying to obtain a lock to the adapter context const CONTEXT_LOCK_TIMEOUT_SECS: u64 = 1; @@ -50,16 +51,6 @@ type WlEglWindowResizeFun = unsafe extern "system" fn( type WlEglWindowDestroyFun = unsafe extern "system" fn(window: *const raw::c_void); -#[cfg(target_os = "android")] -extern "C" { - pub fn ANativeWindow_setBuffersGeometry( - window: *mut raw::c_void, - width: i32, - height: i32, - format: i32, - ) -> i32; -} - type EglLabel = *const raw::c_void; #[allow(clippy::upper_case_acronyms)] @@ -161,7 +152,7 @@ impl Drop for DisplayOwner { fn open_x_display() -> Option<DisplayOwner> { log::debug!("Loading X11 library to get the current display"); unsafe { - let library = libloading::Library::new("libX11.so").ok()?; + let library = find_library(&["libX11.so.6", "libX11.so"])?; let func: libloading::Symbol<XOpenDisplayFun> = library.get(b"XOpenDisplay").unwrap(); let result = func(ptr::null()); ptr::NonNull::new(result).map(|ptr| DisplayOwner { @@ -442,6 +433,45 @@ struct Inner { srgb_kind: SrgbFrameBufferKind, } +// Different calls to `eglGetPlatformDisplay` may return the same `Display`, making it a global +// state of all our `EglContext`s. This forces us to track the number of such context to prevent +// terminating the display if it's currently used by another `EglContext`. +static DISPLAYS_REFERENCE_COUNT: Lazy<Mutex<HashMap<usize, usize>>> = Lazy::new(Default::default); + +fn initialize_display( + egl: &EglInstance, + display: khronos_egl::Display, +) -> Result<(i32, i32), khronos_egl::Error> { + let mut guard = DISPLAYS_REFERENCE_COUNT.lock(); + *guard.entry(display.as_ptr() as usize).or_default() += 1; + + // We don't need to check the reference count here since according to the `eglInitialize` + // documentation, initializing an already initialized EGL display connection has no effect + // besides returning the version numbers. + egl.initialize(display) +} + +fn terminate_display( + egl: &EglInstance, + display: khronos_egl::Display, +) -> Result<(), khronos_egl::Error> { + let key = &(display.as_ptr() as usize); + let mut guard = DISPLAYS_REFERENCE_COUNT.lock(); + let count_ref = guard + .get_mut(key) + .expect("Attempted to decref a display before incref was called"); + + if *count_ref > 1 { + *count_ref -= 1; + + Ok(()) + } else { + guard.remove(key); + + egl.terminate(display) + } +} + impl Inner { fn create( flags: wgt::InstanceFlags, @@ -449,7 +479,7 @@ impl Inner { display: khronos_egl::Display, force_gles_minor_version: wgt::Gles3MinorVersion, ) -> Result<Self, crate::InstanceError> { - let version = egl.initialize(display).map_err(|e| { + let version = initialize_display(&egl, display).map_err(|e| { crate::InstanceError::with_source( String::from("failed to initialize EGL display connection"), e, @@ -618,7 +648,8 @@ impl Drop for Inner { { log::warn!("Error in destroy_context: {:?}", e); } - if let Err(e) = self.egl.instance.terminate(self.egl.display) { + + if let Err(e) = terminate_display(&self.egl.instance, self.egl.display) { log::warn!("Error in terminate: {:?}", e); } } @@ -783,11 +814,12 @@ impl crate::Instance<super::Api> for Instance { (display, Some(Rc::new(display_owner)), WindowKind::AngleX11) } else if client_ext_str.contains("EGL_MESA_platform_surfaceless") { log::warn!("No windowing system present. Using surfaceless platform"); + #[allow(clippy::unnecessary_literal_unwrap)] // This is only a literal on Emscripten let egl = egl1_5.expect("Failed to get EGL 1.5 for surfaceless"); let display = unsafe { egl.get_platform_display( EGL_PLATFORM_SURFACELESS_MESA, - std::ptr::null_mut(), + khronos_egl::DEFAULT_DISPLAY, &[khronos_egl::ATTRIB_NONE], ) } @@ -863,7 +895,12 @@ impl crate::Instance<super::Api> for Instance { .unwrap(); let ret = unsafe { - ANativeWindow_setBuffersGeometry(handle.a_native_window.as_ptr(), 0, 0, format) + ndk_sys::ANativeWindow_setBuffersGeometry( + handle.a_native_window.as_ptr() as *mut ndk_sys::ANativeWindow, + 0, + 0, + format, + ) }; if ret != 0 { diff --git a/third_party/rust/wgpu-hal/src/gles/mod.rs b/third_party/rust/wgpu-hal/src/gles/mod.rs index 646419c7fe..6f41f7c000 100644 --- a/third_party/rust/wgpu-hal/src/gles/mod.rs +++ b/third_party/rust/wgpu-hal/src/gles/mod.rs @@ -251,6 +251,11 @@ struct AdapterShared { next_shader_id: AtomicU32, program_cache: Mutex<ProgramCache>, es: bool, + + /// Result of `gl.get_parameter_i32(glow::MAX_SAMPLES)`. + /// Cached here so it doesn't need to be queried every time texture format capabilities are requested. + /// (this has been shown to be a significant enough overhead) + max_msaa_samples: i32, } pub struct Adapter { @@ -264,6 +269,11 @@ pub struct Device { render_doc: crate::auxil::renderdoc::RenderDoc, } +pub struct ShaderClearProgram { + pub program: glow::Program, + pub color_uniform_location: glow::UniformLocation, +} + pub struct Queue { shared: Arc<AdapterShared>, features: wgt::Features, @@ -271,9 +281,7 @@ pub struct Queue { copy_fbo: glow::Framebuffer, /// Shader program used to clear the screen for [`Workarounds::MESA_I915_SRGB_SHADER_CLEAR`] /// devices. - shader_clear_program: glow::Program, - /// The uniform location of the color uniform in the shader clear program - shader_clear_program_color_uniform_location: glow::UniformLocation, + shader_clear_program: Option<ShaderClearProgram>, /// Keep a reasonably large buffer filled with zeroes, so that we can implement `ClearBuffer` of /// zeroes by copying from it. zero_buffer: glow::Buffer, @@ -366,6 +374,8 @@ impl Texture { /// Returns the `target`, whether the image is 3d and whether the image is a cubemap. fn get_info_from_desc(desc: &TextureDescriptor) -> u32 { match desc.dimension { + // WebGL (1 and 2) as well as some GLES versions do not have 1D textures, so we are + // doing `TEXTURE_2D` instead wgt::TextureDimension::D1 => glow::TEXTURE_2D, wgt::TextureDimension::D2 => { // HACK: detect a cube map; forces cube compatible textures to be cube textures @@ -379,6 +389,43 @@ impl Texture { wgt::TextureDimension::D3 => glow::TEXTURE_3D, } } + + /// More information can be found in issues #1614 and #1574 + fn log_failing_target_heuristics(view_dimension: wgt::TextureViewDimension, target: u32) { + let expected_target = match view_dimension { + wgt::TextureViewDimension::D1 => glow::TEXTURE_2D, + wgt::TextureViewDimension::D2 => glow::TEXTURE_2D, + wgt::TextureViewDimension::D2Array => glow::TEXTURE_2D_ARRAY, + wgt::TextureViewDimension::Cube => glow::TEXTURE_CUBE_MAP, + wgt::TextureViewDimension::CubeArray => glow::TEXTURE_CUBE_MAP_ARRAY, + wgt::TextureViewDimension::D3 => glow::TEXTURE_3D, + }; + + if expected_target == target { + return; + } + + let buffer; + let got = match target { + glow::TEXTURE_2D => "D2", + glow::TEXTURE_2D_ARRAY => "D2Array", + glow::TEXTURE_CUBE_MAP => "Cube", + glow::TEXTURE_CUBE_MAP_ARRAY => "CubeArray", + glow::TEXTURE_3D => "D3", + target => { + buffer = target.to_string(); + &buffer + } + }; + + log::error!( + "wgpu-hal heuristics assumed that the view dimension will be equal to `{got}` rather than `{view_dimension:?}`.\n{}\n{}\n{}\n{}", + "`D2` textures with `depth_or_array_layers == 1` are assumed to have view dimension `D2`", + "`D2` textures with `depth_or_array_layers > 1` are assumed to have view dimension `D2Array`", + "`D2` textures with `depth_or_array_layers == 6` are assumed to have view dimension `Cube`", + "`D2` textures with `depth_or_array_layers > 6 && depth_or_array_layers % 6 == 0` are assumed to have view dimension `CubeArray`", + ); + } } #[derive(Clone, Debug)] diff --git a/third_party/rust/wgpu-hal/src/gles/queue.rs b/third_party/rust/wgpu-hal/src/gles/queue.rs index 6ec553bd29..5db5af9a16 100644 --- a/third_party/rust/wgpu-hal/src/gles/queue.rs +++ b/third_party/rust/wgpu-hal/src/gles/queue.rs @@ -40,10 +40,14 @@ fn get_z_offset(target: u32, base: &crate::TextureCopyBase) -> u32 { impl super::Queue { /// Performs a manual shader clear, used as a workaround for a clearing bug on mesa unsafe fn perform_shader_clear(&self, gl: &glow::Context, draw_buffer: u32, color: [f32; 4]) { - unsafe { gl.use_program(Some(self.shader_clear_program)) }; + let shader_clear = self + .shader_clear_program + .as_ref() + .expect("shader_clear_program should always be set if the workaround is enabled"); + unsafe { gl.use_program(Some(shader_clear.program)) }; unsafe { gl.uniform_4_f32( - Some(&self.shader_clear_program_color_uniform_location), + Some(&shader_clear.color_uniform_location), color[0], color[1], color[2], diff --git a/third_party/rust/wgpu-hal/src/gles/wgl.rs b/third_party/rust/wgpu-hal/src/gles/wgl.rs index 6243430dc2..c9039090b7 100644 --- a/third_party/rust/wgpu-hal/src/gles/wgl.rs +++ b/third_party/rust/wgpu-hal/src/gles/wgl.rs @@ -160,6 +160,9 @@ struct Inner { context: WglContext, } +unsafe impl Send for Inner {} +unsafe impl Sync for Inner {} + pub struct Instance { srgb_capable: bool, inner: Arc<Mutex<Inner>>, |