Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for transparent backbuffer in wgpu winit binding #2684

Merged
merged 4 commits into from
Feb 6, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/eframe/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ NOTE: [`egui-winit`](../egui-winit/CHANGELOG.md), [`egui_glium`](../egui_glium/C
* Fixed window position persistence for Windows ([#2583](https://github.com/emilk/egui/issues/2583)).
* Update to `winit` 0.28, adding support for mac trackpad zoom ([#2654](https://github.com/emilk/egui/pull/2654)).
* Fix bug where the cursor could get stuck using the wrong icon.
* `NativeOptions::transparent` now works with the wgpu backend ([#2684](https://github.com/emilk/egui/pull/2684)).

#### Web:
* Prevent ctrl-P/cmd-P from opening the print dialog ([#2598](https://github.com/emilk/egui/pull/2598)).
Expand Down
1 change: 1 addition & 0 deletions crates/eframe/src/native/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,7 @@ mod wgpu_integration {
self.native_options.wgpu_options.clone(),
self.native_options.multisampling.max(1) as _,
self.native_options.depth_buffer,
self.native_options.transparent,
);
pollster::block_on(painter.set_window(Some(&window)))?;
painter
Expand Down
1 change: 1 addition & 0 deletions crates/egui-wgpu/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ All notable changes to the `egui-wgpu` integration will be noted in this file.
* Return `Err` instead of panic if we can't find a device ([#2428](https://github.com/emilk/egui/pull/2428)).
* `winit::Painter::set_window` is now `async` ([#2434](https://github.com/emilk/egui/pull/2434)).
* `egui-wgpu` now only depends on `epaint` instead of the entire `egui` ([#2438](https://github.com/emilk/egui/pull/2438)).
* `winit::Painter` now supports transparent backbuffer ([#2684](https://github.com/emilk/egui/pull/2684)).


## 0.20.0 - 2022-12-08 - web support
Expand Down
121 changes: 73 additions & 48 deletions crates/egui-wgpu/src/winit.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use std::sync::Arc;

use tracing::error;
use wgpu::{Adapter, Instance, Surface};

use epaint::mutex::RwLock;

use tracing::error;

use crate::{renderer, RenderState, Renderer, SurfaceErrorAction, WgpuConfiguration};

struct SurfaceState {
surface: Surface,
surface: wgpu::Surface,
alpha_mode: wgpu::CompositeAlphaMode,
width: u32,
height: u32,
}
Expand All @@ -19,11 +19,12 @@ struct SurfaceState {
pub struct Painter {
configuration: WgpuConfiguration,
msaa_samples: u32,
support_transparent_backbuffer: bool,
depth_format: Option<wgpu::TextureFormat>,
depth_texture_view: Option<wgpu::TextureView>,

instance: Instance,
adapter: Option<Adapter>,
instance: wgpu::Instance,
adapter: Option<wgpu::Adapter>,
render_state: Option<RenderState>,
surface_state: Option<SurfaceState>,
}
Expand All @@ -41,7 +42,12 @@ impl Painter {
/// [`set_window()`](Self::set_window) once you have
/// a [`winit::window::Window`] with a valid `.raw_window_handle()`
/// associated.
pub fn new(configuration: WgpuConfiguration, msaa_samples: u32, depth_bits: u8) -> Self {
pub fn new(
configuration: WgpuConfiguration,
msaa_samples: u32,
depth_bits: u8,
support_transparent_backbuffer: bool,
) -> Self {
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: configuration.backends,
dx12_shader_compiler: Default::default(), //
Expand All @@ -50,6 +56,7 @@ impl Painter {
Self {
configuration,
msaa_samples,
support_transparent_backbuffer,
depth_format: (depth_bits > 0).then_some(wgpu::TextureFormat::Depth32Float),
depth_texture_view: None,

Expand All @@ -69,7 +76,7 @@ impl Painter {

async fn init_render_state(
&self,
adapter: &Adapter,
adapter: &wgpu::Adapter,
target_format: wgpu::TextureFormat,
) -> Result<RenderState, wgpu::RequestDeviceError> {
adapter
Expand All @@ -94,7 +101,7 @@ impl Painter {
// will have the same format and so this render state will remain valid.
async fn ensure_render_state_for_surface(
&mut self,
surface: &Surface,
surface: &wgpu::Surface,
) -> Result<(), wgpu::RequestDeviceError> {
if self.adapter.is_none() {
self.adapter = self
Expand All @@ -121,34 +128,23 @@ impl Painter {
Ok(())
}

fn configure_surface(&mut self, width_in_pixels: u32, height_in_pixels: u32) {
crate::profile_function!();

let render_state = self
.render_state
.as_ref()
.expect("Render state should exist before surface configuration");
let format = render_state.target_format;

let config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format,
width: width_in_pixels,
height: height_in_pixels,
present_mode: self.configuration.present_mode,
alpha_mode: wgpu::CompositeAlphaMode::Auto,
view_formats: vec![format],
};

let surface_state = self
.surface_state
.as_mut()
.expect("Surface state should exist before surface configuration");
surface_state
.surface
.configure(&render_state.device, &config);
surface_state.width = width_in_pixels;
surface_state.height = height_in_pixels;
fn configure_surface(
surface_state: &SurfaceState,
render_state: &RenderState,
present_mode: wgpu::PresentMode,
) {
surface_state.surface.configure(
&render_state.device,
&wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: render_state.target_format,
width: surface_state.width,
height: surface_state.height,
present_mode,
alpha_mode: surface_state.alpha_mode,
view_formats: vec![render_state.target_format],
},
);
}

/// Updates (or clears) the [`winit::window::Window`] associated with the [`Painter`]
Expand Down Expand Up @@ -188,15 +184,34 @@ impl Painter {

self.ensure_render_state_for_surface(&surface).await?;

let alpha_mode = if self.support_transparent_backbuffer {
let supported_alpha_modes = surface
.get_capabilities(self.adapter.as_ref().unwrap())
.alpha_modes;

// Prefer pre multiplied over post multiplied!
if supported_alpha_modes.contains(&wgpu::CompositeAlphaMode::PreMultiplied) {
wgpu::CompositeAlphaMode::PreMultiplied
} else if supported_alpha_modes
.contains(&wgpu::CompositeAlphaMode::PostMultiplied)
{
wgpu::CompositeAlphaMode::PostMultiplied
} else {
tracing::warn!("Transparent window was requested, but the active wgpu surface does not support a `CompositeAlphaMode` with transparency.");
wgpu::CompositeAlphaMode::Auto
}
} else {
wgpu::CompositeAlphaMode::Auto
};

let size = window.inner_size();
let width = size.width;
let height = size.height;
self.surface_state = Some(SurfaceState {
surface,
width,
height,
width: size.width,
height: size.height,
alpha_mode,
});
self.resize_and_generate_depth_texture_view(width, height);
self.resize_and_generate_depth_texture_view(size.width, size.height);
}
None => {
self.surface_state = None;
Expand All @@ -221,10 +236,17 @@ impl Painter {
width_in_pixels: u32,
height_in_pixels: u32,
) {
self.configure_surface(width_in_pixels, height_in_pixels);
let device = &self.render_state.as_ref().unwrap().device;
let render_state = self.render_state.as_ref().unwrap();
let surface_state = self.surface_state.as_mut().unwrap();

surface_state.width = width_in_pixels;
surface_state.height = height_in_pixels;

Self::configure_surface(surface_state, render_state, self.configuration.present_mode);

self.depth_texture_view = self.depth_format.map(|depth_format| {
device
render_state
.device
.create_texture(&wgpu::TextureDescriptor {
label: Some("egui_depth_texture"),
size: wgpu::Extent3d {
Expand Down Expand Up @@ -269,7 +291,6 @@ impl Painter {
Some(rs) => rs,
None => return,
};
let (width, height) = (surface_state.width, surface_state.height);

let output_frame = {
crate::profile_scope!("get_current_texture");
Expand All @@ -282,7 +303,11 @@ impl Painter {
#[allow(clippy::single_match_else)]
Err(e) => match (*self.configuration.on_surface_error)(e) {
SurfaceErrorAction::RecreateSurface => {
self.configure_surface(width, height);
Self::configure_surface(
surface_state,
render_state,
self.configuration.present_mode,
);
return;
}
SurfaceErrorAction::SkipFrame => {
Expand All @@ -300,7 +325,7 @@ impl Painter {

// Upload all resources for the GPU.
let screen_descriptor = renderer::ScreenDescriptor {
size_in_pixels: [width, height],
size_in_pixels: [surface_state.width, surface_state.height],
pixels_per_point,
};

Expand Down