Support for transparent backbuffer in wgpu winit binding (#2684)

* Support for transparent backbuffer in wgpu winit binding
Choose best fitting composite alpha mode on the fly.

* Compilation fix

* Add line to eframe CHANGELOG

* Attempt to mollify CI: try different way to install apt packages

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
Andreas Reich 2023-02-06 14:16:17 +01:00 committed by GitHub
parent b1e214bbdf
commit b52cd2052f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 107 additions and 50 deletions

View file

@ -16,66 +16,87 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
profile: default profile: default
toolchain: 1.65.0 toolchain: 1.65.0
override: true override: true
- name: Install packages (Linux) - name: Install packages (Linux)
if: runner.os == 'Linux' if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libspeechd-dev libxkbcommon-dev libssl-dev libgtk-3-dev # libgtk-3-dev is used by rfd #uses: awalsh128/cache-apt-pkgs-action@v1.2.2
#TODO(emilk) use upstream when https://github.com/awalsh128/cache-apt-pkgs-action/pull/90 is merged
uses: rerun-io/cache-apt-pkgs-action@59534850182063abf1b2c11bb3686722a12a8397
with:
packages: libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libspeechd-dev libxkbcommon-dev libssl-dev libgtk-3-dev # libgtk-3-dev is used by rfd
version: 1.0
execute_install_scripts: true
- name: Set up cargo cache - name: Set up cargo cache
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
- name: Rustfmt - name: Rustfmt
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: fmt command: fmt
args: --all -- --check args: --all -- --check
- name: Install cargo-cranky - name: Install cargo-cranky
uses: baptiste0928/cargo-install@v1 uses: baptiste0928/cargo-install@v1
with: with:
crate: cargo-cranky crate: cargo-cranky
- name: Check all features - name: Check all features
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: check command: check
args: --locked --all-features args: --locked --all-features
- name: Check default features - name: Check default features
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: check command: check
args: --locked args: --locked
- name: Check no default features - name: Check no default features
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: check command: check
args: --locked --no-default-features --lib args: --locked --no-default-features --lib
- name: Test doc-tests - name: Test doc-tests
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --doc --all-features args: --doc --all-features
- name: cargo doc --lib - name: cargo doc --lib
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: doc command: doc
args: --lib --no-deps --all-features args: --lib --no-deps --all-features
- name: cargo doc --document-private-items - name: cargo doc --document-private-items
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: doc command: doc
args: --document-private-items --no-deps --all-features args: --document-private-items --no-deps --all-features
- name: Test - name: Test
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --all-features args: --all-features
- name: Cranky - name: Cranky
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: cranky command: cranky
args: --all-targets --all-features -- -D warnings args: --all-targets --all-features -- -D warnings
# ---------------------------------------------------------------------------
check_wasm: check_wasm:
name: Check wasm32 + wasm-bindgen name: Check wasm32 + wasm-bindgen
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
@ -129,6 +150,8 @@ jobs:
command: cranky command: cranky
args: --target wasm32-unknown-unknown --all-features -p egui_demo_app --lib -- -D warnings args: --target wasm32-unknown-unknown --all-features -p egui_demo_app --lib -- -D warnings
# ---------------------------------------------------------------------------
cargo-deny: cargo-deny:
name: cargo deny name: cargo deny
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
@ -138,22 +161,29 @@ jobs:
with: with:
rust-version: "1.65.0" rust-version: "1.65.0"
# ---------------------------------------------------------------------------
android: android:
name: android name: android
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
profile: minimal profile: minimal
toolchain: 1.65.0 toolchain: 1.65.0
target: aarch64-linux-android target: aarch64-linux-android
override: true override: true
- name: Set up cargo cache - name: Set up cargo cache
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
- run: cargo check --features wgpu --target aarch64-linux-android - run: cargo check --features wgpu --target aarch64-linux-android
working-directory: crates/eframe working-directory: crates/eframe
# ---------------------------------------------------------------------------
windows: windows:
name: Check Windows name: Check Windows
runs-on: windows-latest runs-on: windows-latest
@ -173,4 +203,3 @@ jobs:
with: with:
command: check command: check
args: --all-targets --all-features args: --all-targets --all-features

View file

@ -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)). * 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)). * 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. * 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: #### Web:
* Prevent ctrl-P/cmd-P from opening the print dialog ([#2598](https://github.com/emilk/egui/pull/2598)). * Prevent ctrl-P/cmd-P from opening the print dialog ([#2598](https://github.com/emilk/egui/pull/2598)).

View file

@ -958,6 +958,7 @@ mod wgpu_integration {
self.native_options.wgpu_options.clone(), self.native_options.wgpu_options.clone(),
self.native_options.multisampling.max(1) as _, self.native_options.multisampling.max(1) as _,
self.native_options.depth_buffer, self.native_options.depth_buffer,
self.native_options.transparent,
); );
pollster::block_on(painter.set_window(Some(&window)))?; pollster::block_on(painter.set_window(Some(&window)))?;
painter painter

View file

@ -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)). * 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)). * `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)). * `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 ## 0.20.0 - 2022-12-08 - web support

View file

@ -1,14 +1,14 @@
use std::sync::Arc; use std::sync::Arc;
use tracing::error;
use wgpu::{Adapter, Instance, Surface};
use epaint::mutex::RwLock; use epaint::mutex::RwLock;
use tracing::error;
use crate::{renderer, RenderState, Renderer, SurfaceErrorAction, WgpuConfiguration}; use crate::{renderer, RenderState, Renderer, SurfaceErrorAction, WgpuConfiguration};
struct SurfaceState { struct SurfaceState {
surface: Surface, surface: wgpu::Surface,
alpha_mode: wgpu::CompositeAlphaMode,
width: u32, width: u32,
height: u32, height: u32,
} }
@ -19,11 +19,12 @@ struct SurfaceState {
pub struct Painter { pub struct Painter {
configuration: WgpuConfiguration, configuration: WgpuConfiguration,
msaa_samples: u32, msaa_samples: u32,
support_transparent_backbuffer: bool,
depth_format: Option<wgpu::TextureFormat>, depth_format: Option<wgpu::TextureFormat>,
depth_texture_view: Option<wgpu::TextureView>, depth_texture_view: Option<wgpu::TextureView>,
instance: Instance, instance: wgpu::Instance,
adapter: Option<Adapter>, adapter: Option<wgpu::Adapter>,
render_state: Option<RenderState>, render_state: Option<RenderState>,
surface_state: Option<SurfaceState>, surface_state: Option<SurfaceState>,
} }
@ -41,7 +42,12 @@ impl Painter {
/// [`set_window()`](Self::set_window) once you have /// [`set_window()`](Self::set_window) once you have
/// a [`winit::window::Window`] with a valid `.raw_window_handle()` /// a [`winit::window::Window`] with a valid `.raw_window_handle()`
/// associated. /// 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 { let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: configuration.backends, backends: configuration.backends,
dx12_shader_compiler: Default::default(), // dx12_shader_compiler: Default::default(), //
@ -50,6 +56,7 @@ impl Painter {
Self { Self {
configuration, configuration,
msaa_samples, msaa_samples,
support_transparent_backbuffer,
depth_format: (depth_bits > 0).then_some(wgpu::TextureFormat::Depth32Float), depth_format: (depth_bits > 0).then_some(wgpu::TextureFormat::Depth32Float),
depth_texture_view: None, depth_texture_view: None,
@ -69,7 +76,7 @@ impl Painter {
async fn init_render_state( async fn init_render_state(
&self, &self,
adapter: &Adapter, adapter: &wgpu::Adapter,
target_format: wgpu::TextureFormat, target_format: wgpu::TextureFormat,
) -> Result<RenderState, wgpu::RequestDeviceError> { ) -> Result<RenderState, wgpu::RequestDeviceError> {
adapter adapter
@ -94,7 +101,7 @@ impl Painter {
// will have the same format and so this render state will remain valid. // will have the same format and so this render state will remain valid.
async fn ensure_render_state_for_surface( async fn ensure_render_state_for_surface(
&mut self, &mut self,
surface: &Surface, surface: &wgpu::Surface,
) -> Result<(), wgpu::RequestDeviceError> { ) -> Result<(), wgpu::RequestDeviceError> {
if self.adapter.is_none() { if self.adapter.is_none() {
self.adapter = self self.adapter = self
@ -121,34 +128,23 @@ impl Painter {
Ok(()) Ok(())
} }
fn configure_surface(&mut self, width_in_pixels: u32, height_in_pixels: u32) { fn configure_surface(
crate::profile_function!(); surface_state: &SurfaceState,
render_state: &RenderState,
let render_state = self present_mode: wgpu::PresentMode,
.render_state ) {
.as_ref() surface_state.surface.configure(
.expect("Render state should exist before surface configuration"); &render_state.device,
let format = render_state.target_format; &wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
let config = wgpu::SurfaceConfiguration { format: render_state.target_format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT, width: surface_state.width,
format, height: surface_state.height,
width: width_in_pixels, present_mode,
height: height_in_pixels, alpha_mode: surface_state.alpha_mode,
present_mode: self.configuration.present_mode, view_formats: vec![render_state.target_format],
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;
} }
/// Updates (or clears) the [`winit::window::Window`] associated with the [`Painter`] /// Updates (or clears) the [`winit::window::Window`] associated with the [`Painter`]
@ -188,15 +184,34 @@ impl Painter {
self.ensure_render_state_for_surface(&surface).await?; 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 size = window.inner_size();
let width = size.width;
let height = size.height;
self.surface_state = Some(SurfaceState { self.surface_state = Some(SurfaceState {
surface, surface,
width, width: size.width,
height, 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 => { None => {
self.surface_state = None; self.surface_state = None;
@ -221,10 +236,17 @@ impl Painter {
width_in_pixels: u32, width_in_pixels: u32,
height_in_pixels: u32, height_in_pixels: u32,
) { ) {
self.configure_surface(width_in_pixels, height_in_pixels); let render_state = self.render_state.as_ref().unwrap();
let device = &self.render_state.as_ref().unwrap().device; 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| { self.depth_texture_view = self.depth_format.map(|depth_format| {
device render_state
.device
.create_texture(&wgpu::TextureDescriptor { .create_texture(&wgpu::TextureDescriptor {
label: Some("egui_depth_texture"), label: Some("egui_depth_texture"),
size: wgpu::Extent3d { size: wgpu::Extent3d {
@ -269,7 +291,6 @@ impl Painter {
Some(rs) => rs, Some(rs) => rs,
None => return, None => return,
}; };
let (width, height) = (surface_state.width, surface_state.height);
let output_frame = { let output_frame = {
crate::profile_scope!("get_current_texture"); crate::profile_scope!("get_current_texture");
@ -282,7 +303,11 @@ impl Painter {
#[allow(clippy::single_match_else)] #[allow(clippy::single_match_else)]
Err(e) => match (*self.configuration.on_surface_error)(e) { Err(e) => match (*self.configuration.on_surface_error)(e) {
SurfaceErrorAction::RecreateSurface => { SurfaceErrorAction::RecreateSurface => {
self.configure_surface(width, height); Self::configure_surface(
surface_state,
render_state,
self.configuration.present_mode,
);
return; return;
} }
SurfaceErrorAction::SkipFrame => { SurfaceErrorAction::SkipFrame => {
@ -300,7 +325,7 @@ impl Painter {
// Upload all resources for the GPU. // Upload all resources for the GPU.
let screen_descriptor = renderer::ScreenDescriptor { let screen_descriptor = renderer::ScreenDescriptor {
size_in_pixels: [width, height], size_in_pixels: [surface_state.width, surface_state.height],
pixels_per_point, pixels_per_point,
}; };