From 8fd6b36e6ced80767d7b216e0393151dee8a2fe0 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 25 Jun 2021 16:58:01 -0400 Subject: [PATCH] hal/egl: add coherence preference, hook up EGL_KHR_debug message callbacks --- wgpu-hal/examples/halmark/main.rs | 15 ++-- wgpu-hal/src/gles/adapter.rs | 6 +- wgpu-hal/src/gles/command.rs | 13 ++-- wgpu-hal/src/gles/device.rs | 78 +++++++++++++++++---- wgpu-hal/src/gles/egl.rs | 113 +++++++++++++++++++++++++----- wgpu-hal/src/gles/mod.rs | 89 ----------------------- wgpu-hal/src/gles/queue.rs | 1 + wgpu-hal/src/lib.rs | 1 + wgpu-hal/src/metal/device.rs | 1 + wgpu-hal/src/vulkan/device.rs | 5 ++ 10 files changed, 189 insertions(+), 133 deletions(-) diff --git a/wgpu-hal/examples/halmark/main.rs b/wgpu-hal/examples/halmark/main.rs index 0fc766fe4b..cd68c053d4 100644 --- a/wgpu-hal/examples/halmark/main.rs +++ b/wgpu-hal/examples/halmark/main.rs @@ -239,7 +239,7 @@ impl Example { label: Some("stage"), size: texture_data.len() as wgt::BufferAddress, usage: hal::BufferUse::MAP_WRITE | hal::BufferUse::COPY_SRC, - memory_flags: hal::MemoryFlag::TRANSIENT, + memory_flags: hal::MemoryFlag::TRANSIENT | hal::MemoryFlag::PREFER_COHERENT, }; let staging_buffer = unsafe { device.create_buffer(&staging_buffer_desc).unwrap() }; unsafe { @@ -342,7 +342,7 @@ impl Example { label: Some("global"), size: mem::size_of::() as wgt::BufferAddress, usage: hal::BufferUse::MAP_WRITE | hal::BufferUse::UNIFORM, - memory_flags: hal::MemoryFlag::empty(), + memory_flags: hal::MemoryFlag::PREFER_COHERENT, }; let global_buffer = unsafe { let buffer = device.create_buffer(&global_buffer_desc).unwrap(); @@ -363,7 +363,7 @@ impl Example { label: Some("local"), size: (MAX_BUNNIES as wgt::BufferAddress) * wgt::BIND_BUFFER_ALIGNMENT, usage: hal::BufferUse::MAP_WRITE | hal::BufferUse::UNIFORM, - memory_flags: hal::MemoryFlag::empty(), + memory_flags: hal::MemoryFlag::PREFER_COHERENT, }; let local_buffer = unsafe { device.create_buffer(&local_buffer_desc).unwrap() }; @@ -474,6 +474,10 @@ impl Example { }) } + fn is_empty(&self) -> bool { + self.bunnies.is_empty() + } + fn exit(mut self) { unsafe { { @@ -732,11 +736,12 @@ fn main() { } }, winit::event::Event::RedrawRequested(_) => { + let ex = example.as_mut().unwrap(); { accum_time += last_frame_inst.elapsed().as_secs_f32(); last_frame_inst = Instant::now(); frame_count += 1; - if frame_count == 100 { + if frame_count == 100 && !ex.is_empty() { println!( "Avg frame time {}ms", accum_time * 1000.0 / frame_count as f32 @@ -745,7 +750,7 @@ fn main() { frame_count = 0; } } - example.as_mut().unwrap().render(); + ex.render(); } winit::event::Event::LoopDestroyed => { example.take().unwrap().exit(); diff --git a/wgpu-hal/src/gles/adapter.rs b/wgpu-hal/src/gles/adapter.rs index 0f5812e341..75db09a837 100644 --- a/wgpu-hal/src/gles/adapter.rs +++ b/wgpu-hal/src/gles/adapter.rs @@ -167,13 +167,13 @@ impl super::Adapter { let ver = Self::parse_version(&version).ok()?; let extensions = gl.supported_extensions(); - log::info!("Extensions: {:?}", extensions); + log::debug!("Extensions: {:#?}", extensions); let shading_language_version = { let sl_version = gl.get_parameter_string(glow::SHADING_LANGUAGE_VERSION); log::info!("SL version: {}", sl_version); - let (sl_major, sl_minor) = Self::parse_version(&version).ok()?; - let value = (sl_major * 100 + sl_minor * 10) as u16; + let (sl_major, sl_minor) = Self::parse_version(&sl_version).ok()?; + let value = sl_major as u16 * 100 + sl_minor as u16 * 10; naga::back::glsl::Version::Embedded(value) }; diff --git a/wgpu-hal/src/gles/command.rs b/wgpu-hal/src/gles/command.rs index a932633324..769078f023 100644 --- a/wgpu-hal/src/gles/command.rs +++ b/wgpu-hal/src/gles/command.rs @@ -403,23 +403,22 @@ impl crate::CommandEncoder for super::CommandEncoder { // issue the clears for (i, cat) in desc.color_attachments.iter().enumerate() { if !cat.ops.contains(crate::AttachmentOp::LOAD) { - let draw_buffer = glow::DRAW_BUFFER0 + i as u32; let c = &cat.clear_value; self.cmd_buffer .commands .push(match cat.target.view.sample_type { wgt::TextureSampleType::Float { .. } => C::ClearColorF( - draw_buffer, - [c.r as f32, c.g as f32, c.r as f32, c.a as f32], + i as u32, + [c.r as f32, c.g as f32, c.b as f32, c.a as f32], ), wgt::TextureSampleType::Depth => unimplemented!(), wgt::TextureSampleType::Uint => C::ClearColorU( - draw_buffer, - [c.r as u32, c.g as u32, c.r as u32, c.a as u32], + i as u32, + [c.r as u32, c.g as u32, c.b as u32, c.a as u32], ), wgt::TextureSampleType::Sint => C::ClearColorI( - draw_buffer, - [c.r as i32, c.g as i32, c.r as i32, c.a as i32], + i as u32, + [c.r as i32, c.g as i32, c.b as i32, c.a as i32], ), }); } diff --git a/wgpu-hal/src/gles/device.rs b/wgpu-hal/src/gles/device.rs index c024b44f9b..e6cc1e8333 100644 --- a/wgpu-hal/src/gles/device.rs +++ b/wgpu-hal/src/gles/device.rs @@ -275,19 +275,19 @@ impl crate::Device for super::Device { } else { glow::ARRAY_BUFFER }; - let map_coherent = false; - let map_flags = glow::MAP_PERSISTENT_BIT - | if map_coherent { - glow::MAP_COHERENT_BIT - } else { - 0 - }; - let mut storage_flags = 0; + + let mut map_flags = glow::MAP_PERSISTENT_BIT; + if desc + .memory_flags + .contains(crate::MemoryFlag::PREFER_COHERENT) + { + map_flags |= glow::MAP_COHERENT_BIT; + } if desc.usage.contains(crate::BufferUse::MAP_READ) { - storage_flags |= map_flags | glow::MAP_READ_BIT; + map_flags |= glow::MAP_READ_BIT; } if desc.usage.contains(crate::BufferUse::MAP_WRITE) { - storage_flags |= map_flags | glow::MAP_WRITE_BIT; + map_flags |= glow::MAP_WRITE_BIT; } let raw = gl.create_buffer().unwrap(); @@ -296,7 +296,7 @@ impl crate::Device for super::Device { .size .try_into() .map_err(|_| crate::DeviceError::OutOfMemory)?; - gl.buffer_storage(target, raw_size, None, storage_flags); + gl.buffer_storage(target, raw_size, None, map_flags); gl.bind_buffer(target, None); Ok(super::Buffer { @@ -513,11 +513,63 @@ impl crate::Device for super::Device { &self, desc: &crate::SamplerDescriptor, ) -> Result { - use super::Sampled; let gl = &self.shared.context; let raw = gl.create_sampler().unwrap(); - super::SamplerBinding(raw).configure_sampling(gl, desc); + + let (min, mag) = + conv::map_filter_modes(desc.min_filter, desc.mag_filter, desc.mipmap_filter); + + gl.sampler_parameter_i32(raw, glow::TEXTURE_MIN_FILTER, min as i32); + gl.sampler_parameter_i32(raw, glow::TEXTURE_MAG_FILTER, mag as i32); + + gl.sampler_parameter_i32( + raw, + glow::TEXTURE_WRAP_S, + conv::map_address_mode(desc.address_modes[0]) as i32, + ); + gl.sampler_parameter_i32( + raw, + glow::TEXTURE_WRAP_T, + conv::map_address_mode(desc.address_modes[1]) as i32, + ); + gl.sampler_parameter_i32( + raw, + glow::TEXTURE_WRAP_R, + conv::map_address_mode(desc.address_modes[2]) as i32, + ); + + if let Some(border_color) = desc.border_color { + let mut border = match border_color { + wgt::SamplerBorderColor::TransparentBlack => [0.0; 4], + wgt::SamplerBorderColor::OpaqueBlack => [0.0, 0.0, 0.0, 1.0], + wgt::SamplerBorderColor::OpaqueWhite => [1.0; 4], + }; + gl.sampler_parameter_f32_slice(raw, glow::TEXTURE_BORDER_COLOR, &mut border); + } + + if let Some(ref range) = desc.lod_clamp { + gl.sampler_parameter_f32(raw, glow::TEXTURE_MIN_LOD, range.start); + gl.sampler_parameter_f32(raw, glow::TEXTURE_MAX_LOD, range.end); + } + + //TODO: `desc.anisotropy_clamp` depends on the downlevel flag + // gl.sampler_parameter_f32(rawow::TEXTURE_MAX_ANISOTROPY, aniso as f32); + + //set_param_float(glow::TEXTURE_LOD_BIAS, info.lod_bias.0); + + if let Some(compare) = desc.compare { + gl.sampler_parameter_i32( + raw, + glow::TEXTURE_COMPARE_MODE, + glow::COMPARE_REF_TO_TEXTURE as i32, + ); + gl.sampler_parameter_i32( + raw, + glow::TEXTURE_COMPARE_FUNC, + conv::map_compare_func(compare) as i32, + ); + } Ok(super::Sampler { raw }) } diff --git a/wgpu-hal/src/gles/egl.rs b/wgpu-hal/src/gles/egl.rs index 5eabfb8bf8..8a2bd1bff0 100644 --- a/wgpu-hal/src/gles/egl.rs +++ b/wgpu-hal/src/gles/egl.rs @@ -1,7 +1,7 @@ use glow::HasContext; use parking_lot::Mutex; -use std::{os::raw, ptr, sync::Arc}; +use std::{ffi::CStr, os::raw, ptr, sync::Arc}; const EGL_PLATFORM_WAYLAND_KHR: u32 = 0x31D8; const EGL_PLATFORM_X11_KHR: u32 = 0x31D5; @@ -41,6 +41,56 @@ extern "C" { ) -> i32; } +type EglLabel = *const raw::c_void; +type EGLDEBUGPROCKHR = Option< + unsafe extern "system" fn( + error: egl::Enum, + command: *const raw::c_char, + message_type: u32, + thread_label: EglLabel, + object_label: EglLabel, + message: *const raw::c_char, + ), +>; + +const EGL_DEBUG_MSG_CRITICAL_KHR: u32 = 0x33B9; +const EGL_DEBUG_MSG_ERROR_KHR: u32 = 0x33BA; +const EGL_DEBUG_MSG_WARN_KHR: u32 = 0x33BB; +const EGL_DEBUG_MSG_INFO_KHR: u32 = 0x33BC; + +type EglDebugMessageControlFun = + unsafe extern "system" fn(proc: EGLDEBUGPROCKHR, attrib_list: *const egl::Attrib) -> raw::c_int; + +unsafe extern "system" fn egl_debug_proc( + error: egl::Enum, + command_raw: *const raw::c_char, + message_type: u32, + _thread_label: EglLabel, + _object_label: EglLabel, + message_raw: *const raw::c_char, +) { + let log_severity = match message_type { + EGL_DEBUG_MSG_CRITICAL_KHR | EGL_DEBUG_MSG_ERROR_KHR => log::Level::Error, + EGL_DEBUG_MSG_WARN_KHR => log::Level::Warn, + EGL_DEBUG_MSG_INFO_KHR => log::Level::Info, + _ => log::Level::Debug, + }; + let command = CStr::from_ptr(command_raw).to_string_lossy(); + let message = if message_raw.is_null() { + "".into() + } else { + CStr::from_ptr(message_raw).to_string_lossy() + }; + + log::log!( + log_severity, + "EGL '{}' code 0x{:x}: {}", + command, + error, + message, + ); +} + fn open_x_display() -> Option<(ptr::NonNull, libloading::Library)> { log::info!("Loading X11 library to get the current display"); unsafe { @@ -112,7 +162,7 @@ fn choose_config( Err(crate::InstanceError) } -fn debug_message_callback(source: u32, gltype: u32, id: u32, severity: u32, message: &str) { +fn gl_debug_message_callback(source: u32, gltype: u32, id: u32, severity: u32, message: &str) { let source_str = match source { glow::DEBUG_SOURCE_API => "API", glow::DEBUG_SOURCE_WINDOW_SYSTEM => "Window System", @@ -146,12 +196,16 @@ fn debug_message_callback(source: u32, gltype: u32, id: u32, severity: u32, mess log::log!( log_severity, - "[{}/{}] ID {} : {}", + "GLES: [{}/{}] ID {} : {}", source_str, type_str, id, message ); + + if log_severity == log::Level::Error { + std::process::exit(1); + } } #[derive(Debug)] @@ -181,11 +235,10 @@ impl Inner { .query_string(Some(display), egl::EXTENSIONS) .unwrap() .to_string_lossy(); - log::info!( - "Display vendor {:?}, version {:?}, extensions: {:?}", - vendor, - version, - display_extensions + log::info!("Display vendor {:?}, version {:?}", vendor, version,); + log::debug!( + "Display extensions: {:#?}", + display_extensions.split_whitespace().collect::>() ); if log::max_level() >= log::LevelFilter::Trace { @@ -211,7 +264,7 @@ impl Inner { egl::CONTEXT_CLIENT_VERSION, 3, // Request GLES 3.0 or higher ]; - if flags.contains(crate::InstanceFlag::VALIDATION) + if flags.contains(crate::InstanceFlag::DEBUG) && wsi_library.is_none() && !cfg!(target_os = "android") { @@ -294,7 +347,10 @@ impl crate::Instance for Instance { Ok(ext) => ext.to_string_lossy().into_owned(), Err(_) => String::new(), }; - log::info!("Client extensions: {:?}", client_ext_str); + log::debug!( + "Client extensions: {:#?}", + client_ext_str.split_whitespace().collect::>() + ); let mut wsi_library = None; @@ -335,6 +391,26 @@ impl crate::Instance for Instance { egl.get_display(egl::DEFAULT_DISPLAY).unwrap() }; + if desc.flags.contains(crate::InstanceFlag::VALIDATION) + && client_ext_str.contains(&"EGL_KHR_debug") + { + log::info!("Enabling EGL debug output"); + let function: EglDebugMessageControlFun = + std::mem::transmute(egl.get_proc_address("eglDebugMessageControlKHR").unwrap()); + let attributes = [ + EGL_DEBUG_MSG_CRITICAL_KHR as egl::Attrib, + 1, + EGL_DEBUG_MSG_ERROR_KHR as egl::Attrib, + 1, + EGL_DEBUG_MSG_WARN_KHR as egl::Attrib, + 1, + EGL_DEBUG_MSG_INFO_KHR as egl::Attrib, + 1, + egl::ATTRIB_NONE, + ]; + (function)(Some(egl_debug_proc), attributes.as_ptr()); + } + let inner = Inner::create(desc.flags, egl, display, wsi_library.as_ref())?; Ok(Instance { @@ -356,6 +432,7 @@ impl crate::Instance for Instance { #[cfg(not(any(target_os = "android", target_os = "macos")))] let (mut temp_xlib_handle, mut temp_xcb_handle); + #[allow(trivial_casts)] let native_window_ptr = match has_handle.raw_window_handle() { #[cfg(not(any(target_os = "android", target_os = "macos")))] Rwh::Xlib(handle) => { @@ -396,9 +473,13 @@ impl crate::Instance for Instance { ) .unwrap(); - let new_inner = - Inner::create(inner.egl.clone(), display, self.wsi_library.as_ref()) - .map_err(|_| w::InitError::UnsupportedWindowHandle)?; + let new_inner = Inner::create( + self.flags, + inner.egl.clone(), + display, + self.wsi_library.as_ref(), + ) + .map_err(|_| crate::InstanceError)?; let old_inner = std::mem::replace(inner.deref_mut(), new_inner); inner.wl_display = Some(handle.display); @@ -526,9 +607,10 @@ impl crate::Instance for Instance { .map_or(ptr::null(), |p| p as *const _) }); - if self.flags.contains(crate::InstanceFlag::DEBUG) && gl.supports_debug() { + if self.flags.contains(crate::InstanceFlag::VALIDATION) && gl.supports_debug() { + log::info!("Enabling GLES debug output"); gl.enable(glow::DEBUG_OUTPUT); - gl.debug_message_callback(debug_message_callback); + gl.debug_message_callback(gl_debug_message_callback); } super::Adapter::expose(gl).into_iter().collect() @@ -581,7 +663,6 @@ impl Surface { crate::SurfaceError::Lost })?; - gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, None); gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(sc.framebuffer)); gl.blit_framebuffer( 0, diff --git a/wgpu-hal/src/gles/mod.rs b/wgpu-hal/src/gles/mod.rs index a042dc20a2..17c7e9ed83 100644 --- a/wgpu-hal/src/gles/mod.rs +++ b/wgpu-hal/src/gles/mod.rs @@ -62,95 +62,6 @@ bitflags::bitflags! { type BindTarget = u32; -trait Sampled { - unsafe fn set_param_float(&self, gl: &glow::Context, key: u32, value: f32); - // see https://github.com/grovesNL/glow/issues/170 - unsafe fn set_param_float_vec(&self, gl: &glow::Context, key: u32, values: &mut [f32]); - unsafe fn set_param_int(&self, gl: &glow::Context, key: u32, value: i32); - - unsafe fn configure_sampling(&self, gl: &glow::Context, desc: &crate::SamplerDescriptor) { - let (min, mag) = - conv::map_filter_modes(desc.min_filter, desc.mag_filter, desc.mipmap_filter); - - self.set_param_int(gl, glow::TEXTURE_MIN_FILTER, min as i32); - self.set_param_int(gl, glow::TEXTURE_MAG_FILTER, mag as i32); - - self.set_param_int( - gl, - glow::TEXTURE_WRAP_S, - conv::map_address_mode(desc.address_modes[0]) as i32, - ); - self.set_param_int( - gl, - glow::TEXTURE_WRAP_T, - conv::map_address_mode(desc.address_modes[1]) as i32, - ); - self.set_param_int( - gl, - glow::TEXTURE_WRAP_R, - conv::map_address_mode(desc.address_modes[2]) as i32, - ); - - if let Some(border_color) = desc.border_color { - let mut border = match border_color { - wgt::SamplerBorderColor::TransparentBlack => [0.0; 4], - wgt::SamplerBorderColor::OpaqueBlack => [0.0, 0.0, 0.0, 1.0], - wgt::SamplerBorderColor::OpaqueWhite => [1.0; 4], - }; - self.set_param_float_vec(gl, glow::TEXTURE_BORDER_COLOR, &mut border); - } - - if let Some(ref range) = desc.lod_clamp { - self.set_param_float(gl, glow::TEXTURE_MIN_LOD, range.start); - self.set_param_float(gl, glow::TEXTURE_MAX_LOD, range.end); - } - - //TODO: `desc.anisotropy_clamp` depends on the downlevel flag - // self.set_param_float(glow::TEXTURE_MAX_ANISOTROPY, aniso as f32); - - //set_param_float(glow::TEXTURE_LOD_BIAS, info.lod_bias.0); - - if let Some(compare) = desc.compare { - self.set_param_int( - gl, - glow::TEXTURE_COMPARE_MODE, - glow::COMPARE_REF_TO_TEXTURE as i32, - ); - self.set_param_int( - gl, - glow::TEXTURE_COMPARE_FUNC, - conv::map_compare_func(compare) as i32, - ); - } - } -} - -struct SamplerBinding(glow::Sampler); -impl Sampled for SamplerBinding { - unsafe fn set_param_float(&self, gl: &glow::Context, key: u32, value: f32) { - gl.sampler_parameter_f32(self.0, key, value); - } - unsafe fn set_param_float_vec(&self, gl: &glow::Context, key: u32, values: &mut [f32]) { - gl.sampler_parameter_f32_slice(self.0, key, values); - } - unsafe fn set_param_int(&self, gl: &glow::Context, key: u32, value: i32) { - gl.sampler_parameter_i32(self.0, key, value); - } -} - -struct SampledTextureBinding(BindTarget); -impl Sampled for SampledTextureBinding { - unsafe fn set_param_float(&self, gl: &glow::Context, key: u32, value: f32) { - gl.tex_parameter_f32(self.0, key, value); - } - unsafe fn set_param_float_vec(&self, gl: &glow::Context, key: u32, values: &mut [f32]) { - gl.tex_parameter_f32_slice(self.0, key, values); - } - unsafe fn set_param_int(&self, gl: &glow::Context, key: u32, value: i32) { - gl.tex_parameter_i32(self.0, key, value); - } -} - #[derive(Debug, Clone, Copy)] enum VertexAttribKind { Float, // glVertexAttribPointer diff --git a/wgpu-hal/src/gles/queue.rs b/wgpu-hal/src/gles/queue.rs index 362c221093..214a644554 100644 --- a/wgpu-hal/src/gles/queue.rs +++ b/wgpu-hal/src/gles/queue.rs @@ -20,6 +20,7 @@ impl super::Queue { unsafe fn reset_state(&self) { let gl = &self.shared.context; gl.use_program(None); + gl.bind_framebuffer(glow::FRAMEBUFFER, None); gl.polygon_offset(0.0, 0.0); gl.disable(glow::DEPTH_TEST); gl.disable(glow::STENCIL_TEST); diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs index 54158e5ee9..c437a04413 100644 --- a/wgpu-hal/src/lib.rs +++ b/wgpu-hal/src/lib.rs @@ -554,6 +554,7 @@ impl From for FormatAspect { bitflags!( pub struct MemoryFlag: u32 { const TRANSIENT = 1; + const PREFER_COHERENT = 2; } ); diff --git a/wgpu-hal/src/metal/device.rs b/wgpu-hal/src/metal/device.rs index 658c071b02..91b2f60a98 100644 --- a/wgpu-hal/src/metal/device.rs +++ b/wgpu-hal/src/metal/device.rs @@ -140,6 +140,7 @@ impl crate::Device for super::Device { let mut options = mtl::MTLResourceOptions::empty(); options |= if map_read || map_write { + // `crate::MemoryFlag::PREFER_COHERENT` is ignored here mtl::MTLResourceOptions::StorageModeShared } else { mtl::MTLResourceOptions::StorageModePrivate diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 51bfbee127..f158ddc3aa 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -557,6 +557,11 @@ impl crate::Device for super::Device { .intersects(crate::BufferUse::MAP_READ | crate::BufferUse::MAP_WRITE) { let mut flags = gpu_alloc::UsageFlags::HOST_ACCESS; + flags.set( + gpu_alloc::UsageFlags::COHERENT, + desc.memory_flags + .contains(crate::MemoryFlag::PREFER_COHERENT), + ); flags.set( gpu_alloc::UsageFlags::DOWNLOAD, desc.usage.contains(crate::BufferUse::MAP_READ),