1
0
mirror of https://github.com/emilk/egui.git synced 2026-09-01 22:30:03 -04:00

[wgpu] Expose wgpu::Adapter via RenderState (#2954)

* [wgpu] Expose adapter, better errors, better docs, more code sharing, use stencil bits

* doc fix

* remove unnecessary unsafe

* handle missing framebuffer format

* better handling of renderstate creation in winit.rs

* remove unnecessary use directive
This commit is contained in:
Andreas Reich
2023-04-25 17:42:13 +02:00
committed by GitHub
parent 20e291d3f6
commit f76eefb98d
9 changed files with 138 additions and 153 deletions

View File

@@ -21,15 +21,80 @@ use std::sync::Arc;
use epaint::mutex::RwLock;
#[derive(thiserror::Error, Debug)]
pub enum WgpuError {
#[error("Failed to create wgpu adapter, no suitable adapter found.")]
NoSuitableAdapterFound,
#[error("There was no valid format for the surface at all.")]
NoSurfaceFormatsAvailable,
#[error(transparent)]
RequestDeviceError(#[from] wgpu::RequestDeviceError),
#[error(transparent)]
CreateSurfaceError(#[from] wgpu::CreateSurfaceError),
}
/// Access to the render state for egui.
#[derive(Clone)]
pub struct RenderState {
/// Wgpu adapter used for rendering.
pub adapter: Arc<wgpu::Adapter>,
/// Wgpu device used for rendering, created from the adapter.
pub device: Arc<wgpu::Device>,
/// Wgpu queue used for rendering, created from the adapter.
pub queue: Arc<wgpu::Queue>,
/// The target texture format used for presenting to the window.
pub target_format: wgpu::TextureFormat,
/// Egui renderer responsible for drawing the UI.
pub renderer: Arc<RwLock<Renderer>>,
}
impl RenderState {
/// Creates a new `RenderState`, containing everything needed for drawing egui with wgpu.
///
/// # Errors
/// Wgpu initialization may fail due to incompatible hardware or driver for a given config.
pub async fn create(
config: &WgpuConfiguration,
instance: &wgpu::Instance,
surface: &wgpu::Surface,
depth_format: Option<wgpu::TextureFormat>,
msaa_samples: u32,
) -> Result<Self, WgpuError> {
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: config.power_preference,
compatible_surface: Some(surface),
force_fallback_adapter: false,
})
.await
.ok_or(WgpuError::NoSuitableAdapterFound)?;
let target_format =
crate::preferred_framebuffer_format(&surface.get_capabilities(&adapter).formats)?;
let (device, queue) = adapter
.request_device(&(*config.device_descriptor)(&adapter), None)
.await?;
let renderer = Renderer::new(&device, target_format, depth_format, msaa_samples);
Ok(RenderState {
adapter: Arc::new(adapter),
device: Arc::new(device),
queue: Arc::new(queue),
target_format,
renderer: Arc::new(RwLock::new(renderer)),
})
}
}
/// Specifies which action should be taken as consequence of a [`wgpu::SurfaceError`]
pub enum SurfaceErrorAction {
/// Do nothing and skip the current frame.
@@ -56,8 +121,6 @@ pub struct WgpuConfiguration {
/// Callback for surface errors.
pub on_surface_error: Arc<dyn Fn(wgpu::SurfaceError) -> SurfaceErrorAction>,
pub depth_format: Option<wgpu::TextureFormat>,
}
impl Default for WgpuConfiguration {
@@ -88,7 +151,6 @@ impl Default for WgpuConfiguration {
present_mode: wgpu::PresentMode::AutoVsync,
power_preference: wgpu::util::power_preference_from_env()
.unwrap_or(wgpu::PowerPreference::HighPerformance),
depth_format: None,
on_surface_error: Arc::new(|err| {
if err == wgpu::SurfaceError::Outdated {
@@ -105,50 +167,40 @@ impl Default for WgpuConfiguration {
}
/// Find the framebuffer format that egui prefers
pub fn preferred_framebuffer_format(formats: &[wgpu::TextureFormat]) -> wgpu::TextureFormat {
///
/// # Errors
/// Returns [`WgpuError::NoSurfaceFormatsAvailable`] if the given list of formats is empty.
pub fn preferred_framebuffer_format(
formats: &[wgpu::TextureFormat],
) -> Result<wgpu::TextureFormat, WgpuError> {
for &format in formats {
if matches!(
format,
wgpu::TextureFormat::Rgba8Unorm | wgpu::TextureFormat::Bgra8Unorm
) {
return format;
return Ok(format);
}
}
formats[0] // take the first
}
// maybe use this-error?
#[derive(Debug)]
pub enum WgpuError {
DeviceError(wgpu::RequestDeviceError),
SurfaceError(wgpu::CreateSurfaceError),
formats
.get(0)
.copied()
.ok_or(WgpuError::NoSurfaceFormatsAvailable)
}
impl std::fmt::Display for WgpuError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(self, f)
/// Take's epi's depth/stencil bits and returns the corresponding wgpu format.
pub fn depth_format_from_bits(depth_buffer: u8, stencil_buffer: u8) -> Option<wgpu::TextureFormat> {
match (depth_buffer, stencil_buffer) {
(0, 8) => Some(wgpu::TextureFormat::Stencil8),
(16, 0) => Some(wgpu::TextureFormat::Depth16Unorm),
(24, 0) => Some(wgpu::TextureFormat::Depth24Plus),
(24, 8) => Some(wgpu::TextureFormat::Depth24PlusStencil8),
(32, 0) => Some(wgpu::TextureFormat::Depth32Float),
(32, 8) => Some(wgpu::TextureFormat::Depth32FloatStencil8),
_ => None,
}
}
impl std::error::Error for WgpuError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
WgpuError::DeviceError(e) => e.source(),
WgpuError::SurfaceError(e) => e.source(),
}
}
}
impl From<wgpu::RequestDeviceError> for WgpuError {
fn from(e: wgpu::RequestDeviceError) -> Self {
Self::DeviceError(e)
}
}
impl From<wgpu::CreateSurfaceError> for WgpuError {
fn from(e: wgpu::CreateSurfaceError) -> Self {
Self::SurfaceError(e)
}
}
// ---------------------------------------------------------------------------
/// Profiling macro for feature "puffin"