@ -15,9 +15,13 @@ use wgpu::util::DeviceExt as _;
///
///
/// `prepare` is called every frame before `paint`, and can use the passed-in [`wgpu::Device`] and
/// `prepare` is called every frame before `paint`, and can use the passed-in [`wgpu::Device`] and
/// [`wgpu::Buffer`] to allocate or modify GPU resources such as buffers.
/// [`wgpu::Buffer`] to allocate or modify GPU resources such as buffers.
/// Additionally, a [`wgpu::CommandEncoder`] is provided in order to allow creation of
/// custom [`wgpu::RenderPass`]/[`wgpu::ComputePass`] or perform buffer/texture copies
/// which may serve as preparation to the final `paint`.
/// (This allows reusing the same [`wgpu::CommandEncoder`] for all callbacks and egui rendering itself)
///
///
/// `paint` is called after `prepare` and is given access to the the [`wgpu::RenderPass`] so that it
/// `paint` is called after `prepare` and is given access to the [`wgpu::RenderPass`] so that it
/// can issue draw commands.
/// can issue draw commands into the same [`wgpu::RenderPass`] that is used for all other egui elements .
///
///
/// The final argument of both the `prepare` and `paint` callbacks is a the
/// The final argument of both the `prepare` and `paint` callbacks is a the
/// [`paint_callback_resources`][crate::renderer::Renderer::paint_callback_resources].
/// [`paint_callback_resources`][crate::renderer::Renderer::paint_callback_resources].
@ -33,7 +37,8 @@ pub struct CallbackFn {
paint : Box < PaintCallback > ,
paint : Box < PaintCallback > ,
}
}
type PrepareCallback = dyn Fn ( & wgpu ::Device , & wgpu ::Queue , & mut TypeMap ) + Sync + Send ;
type PrepareCallback =
dyn Fn ( & wgpu ::Device , & wgpu ::Queue , & mut wgpu ::CommandEncoder , & mut TypeMap ) + Sync + Send ;
type PaintCallback =
type PaintCallback =
dyn for < 'a , 'b > Fn ( PaintCallbackInfo , & 'a mut wgpu ::RenderPass < 'b > , & 'b TypeMap ) + Sync + Send ;
dyn for < 'a , 'b > Fn ( PaintCallbackInfo , & 'a mut wgpu ::RenderPass < 'b > , & 'b TypeMap ) + Sync + Send ;
@ -41,7 +46,7 @@ type PaintCallback =
impl Default for CallbackFn {
impl Default for CallbackFn {
fn default ( ) -> Self {
fn default ( ) -> Self {
CallbackFn {
CallbackFn {
prepare : Box ::new ( | _ , _ , _ | ( ) ) ,
prepare : Box ::new ( | _ , _ , _ , _ | ( ) ) ,
paint : Box ::new ( | _ , _ , _ | ( ) ) ,
paint : Box ::new ( | _ , _ , _ | ( ) ) ,
}
}
}
}
@ -55,7 +60,10 @@ impl CallbackFn {
/// Set the prepare callback
/// Set the prepare callback
pub fn prepare < F > ( mut self , prepare : F ) -> Self
pub fn prepare < F > ( mut self , prepare : F ) -> Self
where
where
F : Fn ( & wgpu ::Device , & wgpu ::Queue , & mut TypeMap ) + Sync + Send + 'static ,
F : Fn ( & wgpu ::Device , & wgpu ::Queue , & mut wgpu ::CommandEncoder , & mut TypeMap )
+ Sync
+ Send
+ 'static ,
{
{
self . prepare = Box ::new ( prepare ) as _ ;
self . prepare = Box ::new ( prepare ) as _ ;
self
self
@ -122,7 +130,6 @@ struct SizedBuffer {
/// Renderer for a egui based GUI.
/// Renderer for a egui based GUI.
pub struct Renderer {
pub struct Renderer {
pipeline : wgpu ::RenderPipeline ,
pipeline : wgpu ::RenderPipeline ,
depth_texture : Option < ( wgpu ::Texture , wgpu ::TextureView ) > ,
index_buffers : Vec < SizedBuffer > ,
index_buffers : Vec < SizedBuffer > ,
vertex_buffers : Vec < SizedBuffer > ,
vertex_buffers : Vec < SizedBuffer > ,
uniform_buffer : SizedBuffer ,
uniform_buffer : SizedBuffer ,
@ -141,13 +148,13 @@ pub struct Renderer {
impl Renderer {
impl Renderer {
/// Creates a renderer for a egui UI.
/// Creates a renderer for a egui UI.
///
///
/// `output_format` should preferably be [`wgpu::TextureFormat::Rgba8Unorm`] or
/// `output_color_ format` should preferably be [`wgpu::TextureFormat::Rgba8Unorm`] or
/// [`wgpu::TextureFormat::Bgra8Unorm`], i.e. in gamma-space.
/// [`wgpu::TextureFormat::Bgra8Unorm`], i.e. in gamma-space.
pub fn new (
pub fn new (
device : & wgpu ::Device ,
device : & wgpu ::Device ,
output_format : wgpu ::TextureFormat ,
output_color_format : wgpu ::TextureFormat ,
output_depth_format : Option < wgpu ::TextureFormat > ,
msaa_samples : u32 ,
msaa_samples : u32 ,
depth_bits : u8 ,
) -> Self {
) -> Self {
let shader = wgpu ::ShaderModuleDescriptor {
let shader = wgpu ::ShaderModuleDescriptor {
label : Some ( "egui" ) ,
label : Some ( "egui" ) ,
@ -225,8 +232,8 @@ impl Renderer {
push_constant_ranges : & [ ] ,
push_constant_ranges : & [ ] ,
} ) ;
} ) ;
let depth_stencil = ( depth_bits > 0 ) . then ( | | wgpu ::DepthStencilState {
let depth_stencil = output_depth_format . map ( | format | wgpu ::DepthStencilState {
format : wgpu ::TextureFormat ::Depth32Float ,
format ,
depth_write_enabled : false ,
depth_write_enabled : false ,
depth_compare : wgpu ::CompareFunction ::Always ,
depth_compare : wgpu ::CompareFunction ::Always ,
stencil : wgpu ::StencilState ::default ( ) ,
stencil : wgpu ::StencilState ::default ( ) ,
@ -266,14 +273,14 @@ impl Renderer {
fragment : Some ( wgpu ::FragmentState {
fragment : Some ( wgpu ::FragmentState {
module : & module ,
module : & module ,
entry_point : if output_format . describe ( ) . srgb {
entry_point : if output_color_ format . describe ( ) . srgb {
tracing ::warn ! ( "Detected a linear (sRGBA aware) framebuffer {:?}. egui prefers Rgba8Unorm or Bgra8Unorm" , output_format ) ;
tracing ::warn ! ( "Detected a linear (sRGBA aware) framebuffer {:?}. egui prefers Rgba8Unorm or Bgra8Unorm" , output_color_ format ) ;
"fs_main_linear_framebuffer"
"fs_main_linear_framebuffer"
} else {
} else {
"fs_main_gamma_framebuffer" // this is what we prefer
"fs_main_gamma_framebuffer" // this is what we prefer
} ,
} ,
targets : & [ Some ( wgpu ::ColorTargetState {
targets : & [ Some ( wgpu ::ColorTargetState {
format : output_format ,
format : output_color_ format ,
blend : Some ( wgpu ::BlendState {
blend : Some ( wgpu ::BlendState {
color : wgpu ::BlendComponent {
color : wgpu ::BlendComponent {
src_factor : wgpu ::BlendFactor ::One ,
src_factor : wgpu ::BlendFactor ::One ,
@ -302,75 +309,11 @@ impl Renderer {
textures : HashMap ::new ( ) ,
textures : HashMap ::new ( ) ,
next_user_texture_id : 0 ,
next_user_texture_id : 0 ,
paint_callback_resources : TypeMap ::default ( ) ,
paint_callback_resources : TypeMap ::default ( ) ,
depth_texture : None ,
}
}
}
}
pub fn update_depth_texture ( & mut self , device : & wgpu ::Device , width : u32 , height : u32 ) {
// TODO(wumpf) don't recreate texture if size hasn't changed
let texture = device . create_texture ( & wgpu ::TextureDescriptor {
label : Some ( "egui_depth_texture" ) ,
size : wgpu ::Extent3d {
width ,
height ,
depth_or_array_layers : 1 ,
} ,
mip_level_count : 1 ,
sample_count : 1 ,
dimension : wgpu ::TextureDimension ::D2 ,
format : wgpu ::TextureFormat ::Depth32Float ,
usage : wgpu ::TextureUsages ::RENDER_ATTACHMENT | wgpu ::TextureUsages ::TEXTURE_BINDING ,
} ) ;
let view = texture . create_view ( & wgpu ::TextureViewDescriptor ::default ( ) ) ;
self . depth_texture = Some ( ( texture , view ) ) ;
}
/// Executes the renderer on its own render pass.
pub fn render (
& self ,
encoder : & mut wgpu ::CommandEncoder ,
color_attachment : & wgpu ::TextureView ,
paint_jobs : & [ egui ::epaint ::ClippedPrimitive ] ,
screen_descriptor : & ScreenDescriptor ,
clear_color : Option < wgpu ::Color > ,
) {
let load_operation = if let Some ( color ) = clear_color {
wgpu ::LoadOp ::Clear ( color )
} else {
wgpu ::LoadOp ::Load
} ;
let depth_stencil_attachment = self . depth_texture . as_ref ( ) . map ( | ( _texture , view ) | {
wgpu ::RenderPassDepthStencilAttachment {
view ,
depth_ops : Some ( wgpu ::Operations {
load : wgpu ::LoadOp ::Clear ( 1.0 ) ,
store : true ,
} ) ,
stencil_ops : None ,
}
} ) ;
let mut render_pass = encoder . begin_render_pass ( & wgpu ::RenderPassDescriptor {
color_attachments : & [ Some ( wgpu ::RenderPassColorAttachment {
view : color_attachment ,
resolve_target : None ,
ops : wgpu ::Operations {
load : load_operation ,
store : true ,
} ,
} ) ] ,
depth_stencil_attachment ,
label : Some ( "egui_render" ) ,
} ) ;
self . render_onto_renderpass ( & mut render_pass , paint_jobs , screen_descriptor ) ;
}
/// Executes the egui renderer onto an existing wgpu renderpass.
/// Executes the egui renderer onto an existing wgpu renderpass.
pub fn render_onto_renderpass < 'rp > (
pub fn render < 'rp > (
& 'rp self ,
& 'rp self ,
render_pass : & mut wgpu ::RenderPass < 'rp > ,
render_pass : & mut wgpu ::RenderPass < 'rp > ,
paint_jobs : & [ egui ::epaint ::ClippedPrimitive ] ,
paint_jobs : & [ egui ::epaint ::ClippedPrimitive ] ,
@ -760,6 +703,7 @@ impl Renderer {
& mut self ,
& mut self ,
device : & wgpu ::Device ,
device : & wgpu ::Device ,
queue : & wgpu ::Queue ,
queue : & wgpu ::Queue ,
encoder : & mut wgpu ::CommandEncoder ,
paint_jobs : & [ egui ::epaint ::ClippedPrimitive ] ,
paint_jobs : & [ egui ::epaint ::ClippedPrimitive ] ,
screen_descriptor : & ScreenDescriptor ,
screen_descriptor : & ScreenDescriptor ,
) {
) {
@ -821,7 +765,7 @@ impl Renderer {
continue ;
continue ;
} ;
} ;
( cbfn . prepare ) ( device , queue , & mut self . paint_callback_resources ) ;
( cbfn . prepare ) ( device , queue , encoder , & mut self . paint_callback_resources ) ;
}
}
}
}
}
}