diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 76a105423..32c459740 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -69,7 +69,7 @@ jobs: fail-fast: false matrix: features: [ - 'multi_threaded', + 'immutable_ctx', 'manage_clipboard', 'open_url', 'manage_clipboard,open_url', @@ -121,7 +121,7 @@ jobs: - uses: actions-rs/cargo@v1 with: command: doc - args: --all + args: --all --features "bevy/x11" env: RUSTDOCFLAGS: -D warnings diff --git a/Cargo.toml b/Cargo.toml index 65b9f9b84..f60dd5e49 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,21 +14,14 @@ all-features = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] default = ["manage_clipboard", "open_url"] +immutable_ctx = [] manage_clipboard = ["arboard", "thread_local"] -multi_threaded = ["egui/multi_threaded"] open_url = ["webbrowser"] [dependencies] -bevy = { version = "0.7", default-features = false, features = [ - "bevy_render", - "bevy_winit", - "bevy_core_pipeline", -] } -egui = { version = "0.17", features = ["convert_bytemuck"] } +bevy = { version = "0.7", default-features = false, features = ["bevy_render", "bevy_core_pipeline"] } +egui = { git = "https://github.com/emilk/egui.git", features = ["bytemuck"] } webbrowser = { version = "0.6.0", optional = true } -winit = { version = "0.26.0", features = ["x11"], default-features = false } -bytemuck = { version = "1.7.0", features = ["derive"] } -wgpu = "0.12.0" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] arboard = { version = "2.0.1", optional = true } diff --git a/src/egui_node.rs b/src/egui_node.rs index bf79ed361..60a006276 100644 --- a/src/egui_node.rs +++ b/src/egui_node.rs @@ -6,11 +6,13 @@ use bevy::{ render_resource::{ std140::AsStd140, BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, BlendComponent, BlendFactor, BlendOperation, BlendState, Buffer, - BufferSize, BufferUsages, ColorTargetState, ColorWrites, Extent3d, FrontFace, - IndexFormat, LoadOp, MultisampleState, Operations, PipelineLayoutDescriptor, - PrimitiveState, RenderPassColorAttachment, RenderPassDescriptor, RenderPipeline, - ShaderStages, TextureDimension, TextureFormat, TextureSampleType, TextureViewDimension, - VertexAttribute, VertexFormat, VertexStepMode, + BufferAddress, BufferBindingType, BufferDescriptor, BufferSize, BufferUsages, + ColorTargetState, ColorWrites, Extent3d, FrontFace, IndexFormat, LoadOp, + MultisampleState, Operations, PipelineLayoutDescriptor, PrimitiveState, + RawFragmentState, RawRenderPipelineDescriptor, RawVertexBufferLayout, RawVertexState, + RenderPassColorAttachment, RenderPassDescriptor, RenderPipeline, SamplerBindingType, + ShaderModuleDescriptor, ShaderSource, ShaderStages, TextureDimension, TextureFormat, + TextureSampleType, TextureViewDimension, VertexAttribute, VertexFormat, VertexStepMode, }, renderer::{RenderContext, RenderDevice, RenderQueue}, texture::{BevyDefault, Image}, @@ -18,7 +20,6 @@ use bevy::{ }, window::WindowId, }; -use wgpu::{BufferDescriptor, SamplerBindingType, ShaderModuleDescriptor, ShaderSource}; use crate::render_systems::{ EguiTexture, EguiTextureBindGroups, EguiTransform, EguiTransforms, ExtractedEguiContext, @@ -49,7 +50,7 @@ impl FromWorld for EguiPipeline { binding: 0, visibility: ShaderStages::VERTEX, ty: BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, + ty: BufferBindingType::Uniform, has_dynamic_offset: true, min_binding_size: Some( BufferSize::new(EguiTransform::std140_size_static() as u64).unwrap(), @@ -87,64 +88,63 @@ impl FromWorld for EguiPipeline { push_constant_ranges: &[], }); - let render_pipeline = - render_device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("egui render pipeline"), - layout: Some(&pipeline_layout), - vertex: wgpu::VertexState { - module: &shader_module, - entry_point: "vs_main", - buffers: &[wgpu::VertexBufferLayout { - array_stride: 20, - step_mode: VertexStepMode::Vertex, - attributes: &[ - VertexAttribute { - format: VertexFormat::Float32x2, - offset: 0, - shader_location: 0, - }, - VertexAttribute { - format: VertexFormat::Float32x2, - offset: 8, - shader_location: 1, - }, - VertexAttribute { - format: VertexFormat::Unorm8x4, - offset: 16, - shader_location: 2, - }, - ], - }], - }, - fragment: Some(wgpu::FragmentState { - module: &shader_module, - entry_point: "fs_main", - targets: &[ColorTargetState { - format: TextureFormat::bevy_default(), - blend: Some(BlendState { - color: BlendComponent { - src_factor: BlendFactor::One, - dst_factor: BlendFactor::OneMinusSrcAlpha, - operation: BlendOperation::Add, - }, - alpha: BlendComponent { - src_factor: BlendFactor::One, - dst_factor: BlendFactor::OneMinusSrcAlpha, - operation: BlendOperation::Add, - }, - }), - write_mask: ColorWrites::ALL, - }], - }), - primitive: PrimitiveState { - front_face: FrontFace::Cw, - cull_mode: None, - ..Default::default() - }, - depth_stencil: None, - multisample: MultisampleState::default(), - multiview: None, - }); + let render_pipeline = render_device.create_render_pipeline(&RawRenderPipelineDescriptor { + label: Some("egui render pipeline"), + layout: Some(&pipeline_layout), + vertex: RawVertexState { + module: &shader_module, + entry_point: "vs_main", + buffers: &[RawVertexBufferLayout { + array_stride: 20, + step_mode: VertexStepMode::Vertex, + attributes: &[ + VertexAttribute { + format: VertexFormat::Float32x2, + offset: 0, + shader_location: 0, + }, + VertexAttribute { + format: VertexFormat::Float32x2, + offset: 8, + shader_location: 1, + }, + VertexAttribute { + format: VertexFormat::Unorm8x4, + offset: 16, + shader_location: 2, + }, + ], + }], + }, + fragment: Some(RawFragmentState { + module: &shader_module, + entry_point: "fs_main", + targets: &[ColorTargetState { + format: TextureFormat::bevy_default(), + blend: Some(BlendState { + color: BlendComponent { + src_factor: BlendFactor::One, + dst_factor: BlendFactor::OneMinusSrcAlpha, + operation: BlendOperation::Add, + }, + alpha: BlendComponent { + src_factor: BlendFactor::One, + dst_factor: BlendFactor::OneMinusSrcAlpha, + operation: BlendOperation::Add, + }, + }), + write_mask: ColorWrites::ALL, + }], + }), + primitive: PrimitiveState { + front_face: FrontFace::Cw, + cull_mode: None, + ..Default::default() + }, + depth_stencil: None, + multisample: MultisampleState::default(), + multiview: None, + }); EguiPipeline { pipeline: render_pipeline, @@ -215,12 +215,23 @@ impl Node for EguiNode { self.vertex_data.clear(); self.index_data.clear(); - for egui::ClippedMesh(rect, triangles) in &egui_paint_jobs { + for egui::epaint::ClippedPrimitive { + clip_rect, + primitive, + } in &egui_paint_jobs + { + let mesh = match primitive { + egui::epaint::Primitive::Mesh(mesh) => mesh, + egui::epaint::Primitive::Callback(_) => { + unimplemented!("Paint callbacks aren't supported") + } + }; + let (x, y, w, h) = ( - (rect.min.x * scale_factor).round() as u32, - (rect.min.y * scale_factor).round() as u32, - (rect.width() * scale_factor).round() as u32, - (rect.height() * scale_factor).round() as u32, + (clip_rect.min.x * scale_factor).round() as u32, + (clip_rect.min.y * scale_factor).round() as u32, + (clip_rect.width() * scale_factor).round() as u32, + (clip_rect.height() * scale_factor).round() as u32, ); if w < 1 @@ -232,17 +243,17 @@ impl Node for EguiNode { } self.vertex_data - .extend_from_slice(cast_slice(triangles.vertices.as_slice())); - let indices_with_offset = triangles + .extend_from_slice(cast_slice::<_, u8>(mesh.vertices.as_slice())); + let indices_with_offset = mesh .indices .iter() .map(|i| i + index_offset) .collect::>(); self.index_data .extend_from_slice(cast_slice(indices_with_offset.as_slice())); - index_offset += triangles.vertices.len() as u32; + index_offset += mesh.vertices.len() as u32; - let texture_handle = match triangles.texture_id { + let texture_handle = match mesh.texture_id { egui::TextureId::Managed(id) => EguiTexture::Managed(self.window_id, id), egui::TextureId::User(id) => EguiTexture::User(id), }; @@ -250,7 +261,7 @@ impl Node for EguiNode { let x_viewport_clamp = (x + w).saturating_sub(window_size.physical_width as u32); let y_viewport_clamp = (y + h).saturating_sub(window_size.physical_height as u32); self.draw_commands.push(DrawCommand { - vertices_count: triangles.indices.len(), + vertices_count: mesh.indices.len(), egui_texture: texture_handle, clipping_zone: ( x, @@ -269,7 +280,7 @@ impl Node for EguiNode { }; self.vertex_buffer = Some(render_device.create_buffer(&BufferDescriptor { label: Some("egui vertex buffer"), - size: self.vertex_buffer_capacity as wgpu::BufferAddress, + size: self.vertex_buffer_capacity as BufferAddress, usage: BufferUsages::COPY_DST | BufferUsages::VERTEX, mapped_at_creation: false, })); @@ -282,7 +293,7 @@ impl Node for EguiNode { }; self.index_buffer = Some(render_device.create_buffer(&BufferDescriptor { label: Some("egui index buffer"), - size: self.index_buffer_capacity as wgpu::BufferAddress, + size: self.index_buffer_capacity as BufferAddress, usage: BufferUsages::COPY_DST | BufferUsages::INDEX, mapped_at_creation: false, })); @@ -388,11 +399,11 @@ impl Node for EguiNode { pub fn as_color_image(image: egui::ImageData) -> egui::ColorImage { match image { egui::ImageData::Color(image) => image, - egui::ImageData::Alpha(image) => alpha_image_as_color_image(&image), + egui::ImageData::Font(image) => alpha_image_as_color_image(&image), } } -pub fn alpha_image_as_color_image(image: &egui::AlphaImage) -> egui::ColorImage { +pub fn alpha_image_as_color_image(image: &egui::FontImage) -> egui::ColorImage { let gamma = 1.0; egui::ColorImage { size: image.size, diff --git a/src/lib.rs b/src/lib.rs index 293dc9c8a..aa4155e13 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -230,9 +230,9 @@ impl EguiContext { /// Egui context of the primary window. /// - /// This function is only available when the `multi_threaded` feature is enabled. + /// This function is only available when the `immutable_ctx` feature is enabled. /// The preferable way is to use `ctx_mut` to avoid unpredictable blocking inside UI systems. - #[cfg(feature = "multi_threaded")] + #[cfg(feature = "immutable_ctx")] #[must_use] #[track_caller] pub fn ctx(&self) -> &egui::Context { @@ -243,10 +243,10 @@ impl EguiContext { /// If you want to display UI on a non-primary window, make sure to set up the render graph by /// calling [`setup_pipeline`]. /// - /// This function is only available when the `multi_threaded` feature is enabled. + /// This function is only available when the `immutable_ctx` feature is enabled. /// The preferable way is to use `ctx_for_window_mut` to avoid unpredictable blocking inside UI /// systems. - #[cfg(feature = "multi_threaded")] + #[cfg(feature = "immutable_ctx")] #[must_use] #[track_caller] pub fn ctx_for_window(&self, window: WindowId) -> &egui::Context { @@ -258,10 +258,10 @@ impl EguiContext { /// Fallible variant of [`EguiContext::ctx_for_window`]. Make sure to set up the render graph by /// calling [`setup_pipeline`]. /// - /// This function is only available when the `multi_threaded` feature is enabled. + /// This function is only available when the `immutable_ctx` feature is enabled. /// The preferable way is to use `try_ctx_for_window_mut` to avoid unpredictable blocking inside /// UI systems. - #[cfg(feature = "multi_threaded")] + #[cfg(feature = "immutable_ctx")] #[must_use] pub fn try_ctx_for_window(&self, window: WindowId) -> Option<&egui::Context> { self.ctx.get(&window) @@ -293,7 +293,7 @@ impl EguiContext { } /// Allows to get multiple contexts at the same time. This function is useful when you want - /// to get multiple window contexts without using the `multi_threaded` feature. + /// to get multiple window contexts without using the `immutable_ctx` feature. /// /// # Panics /// diff --git a/src/render_systems.rs b/src/render_systems.rs index 81128fa43..d08cfd748 100644 --- a/src/render_systems.rs +++ b/src/render_systems.rs @@ -1,21 +1,22 @@ +use crate::{ + egui_node::EguiPipeline, EguiContext, EguiManagedTextures, EguiRenderOutput, EguiSettings, + WindowSize, +}; use bevy::{ asset::HandleId, prelude::*, render::{ render_asset::RenderAssets, - render_resource::{std140::AsStd140, BindGroup, BufferId, DynamicUniformVec}, + render_resource::{ + std140::AsStd140, BindGroup, BindGroupDescriptor, BindGroupEntry, BindingResource, + BufferId, DynamicUniformVec, + }, renderer::{RenderDevice, RenderQueue}, texture::Image, }, utils::HashMap, window::WindowId, }; -use wgpu::{BindGroupDescriptor, BindGroupEntry, BindingResource}; - -use crate::{ - egui_node::EguiPipeline, EguiContext, EguiManagedTextures, EguiRenderOutput, EguiSettings, - WindowSize, -}; pub(crate) struct ExtractedRenderOutput(pub HashMap); pub(crate) struct ExtractedWindowSizes(pub HashMap); diff --git a/src/systems.rs b/src/systems.rs index 4f8f0e9c9..51c71869c 100644 --- a/src/systems.rs +++ b/src/systems.rs @@ -14,13 +14,12 @@ use bevy::{ mouse::{MouseButton, MouseButtonInput, MouseScrollUnit, MouseWheel}, ElementState, Input, }, - prelude::{EventReader, NonSend}, + prelude::EventReader, utils::HashMap, window::{ CursorEntered, CursorLeft, CursorMoved, ReceivedCharacter, RequestRedraw, WindowCreated, WindowFocused, WindowId, Windows, }, - winit::WinitWindows, }; #[derive(SystemParam)] @@ -339,7 +338,7 @@ pub fn process_output( mut egui_output: ResMut>, mut egui_render_output: ResMut>, #[cfg(feature = "manage_clipboard")] mut egui_clipboard: ResMut, - winit_windows: Option>, + mut windows: Option>, mut event: EventWriter, ) { for (window_id, ctx) in egui_context.ctx.iter_mut() { @@ -362,11 +361,11 @@ pub fn process_output( egui_clipboard.set_contents(&platform_output.copied_text); } - if let Some(ref winit_windows) = winit_windows { - if let Some(winit_window) = winit_windows.get_window(*window_id) { - winit_window.set_cursor_icon( + if let Some(ref mut windows) = windows { + if let Some(window) = windows.get_mut(*window_id) { + window.set_cursor_icon( egui_to_winit_cursor_icon(platform_output.cursor_icon) - .unwrap_or(winit::window::CursorIcon::Default), + .unwrap_or(bevy::window::CursorIcon::Default), ); } } @@ -389,32 +388,42 @@ pub fn process_output( } } -fn egui_to_winit_cursor_icon(cursor_icon: egui::CursorIcon) -> Option { +fn egui_to_winit_cursor_icon(cursor_icon: egui::CursorIcon) -> Option { match cursor_icon { - egui::CursorIcon::Default => Some(winit::window::CursorIcon::Default), - egui::CursorIcon::PointingHand => Some(winit::window::CursorIcon::Hand), - egui::CursorIcon::ResizeHorizontal => Some(winit::window::CursorIcon::EwResize), - egui::CursorIcon::ResizeNeSw => Some(winit::window::CursorIcon::NeswResize), - egui::CursorIcon::ResizeNwSe => Some(winit::window::CursorIcon::NwseResize), - egui::CursorIcon::ResizeVertical => Some(winit::window::CursorIcon::NsResize), - egui::CursorIcon::Text => Some(winit::window::CursorIcon::Text), - egui::CursorIcon::Grab => Some(winit::window::CursorIcon::Grab), - egui::CursorIcon::Grabbing => Some(winit::window::CursorIcon::Grabbing), - egui::CursorIcon::ContextMenu => Some(winit::window::CursorIcon::ContextMenu), - egui::CursorIcon::Help => Some(winit::window::CursorIcon::Help), - egui::CursorIcon::Progress => Some(winit::window::CursorIcon::Progress), - egui::CursorIcon::Wait => Some(winit::window::CursorIcon::Wait), - egui::CursorIcon::Cell => Some(winit::window::CursorIcon::Cell), - egui::CursorIcon::Crosshair => Some(winit::window::CursorIcon::Crosshair), - egui::CursorIcon::VerticalText => Some(winit::window::CursorIcon::VerticalText), - egui::CursorIcon::Alias => Some(winit::window::CursorIcon::Alias), - egui::CursorIcon::Copy => Some(winit::window::CursorIcon::Copy), - egui::CursorIcon::Move => Some(winit::window::CursorIcon::Move), - egui::CursorIcon::NoDrop => Some(winit::window::CursorIcon::NoDrop), - egui::CursorIcon::NotAllowed => Some(winit::window::CursorIcon::NotAllowed), - egui::CursorIcon::AllScroll => Some(winit::window::CursorIcon::AllScroll), - egui::CursorIcon::ZoomIn => Some(winit::window::CursorIcon::ZoomIn), - egui::CursorIcon::ZoomOut => Some(winit::window::CursorIcon::ZoomOut), + egui::CursorIcon::Default => Some(bevy::window::CursorIcon::Default), + egui::CursorIcon::PointingHand => Some(bevy::window::CursorIcon::Hand), + egui::CursorIcon::ResizeHorizontal => Some(bevy::window::CursorIcon::EwResize), + egui::CursorIcon::ResizeNeSw => Some(bevy::window::CursorIcon::NeswResize), + egui::CursorIcon::ResizeNwSe => Some(bevy::window::CursorIcon::NwseResize), + egui::CursorIcon::ResizeVertical => Some(bevy::window::CursorIcon::NsResize), + egui::CursorIcon::Text => Some(bevy::window::CursorIcon::Text), + egui::CursorIcon::Grab => Some(bevy::window::CursorIcon::Grab), + egui::CursorIcon::Grabbing => Some(bevy::window::CursorIcon::Grabbing), + egui::CursorIcon::ContextMenu => Some(bevy::window::CursorIcon::ContextMenu), + egui::CursorIcon::Help => Some(bevy::window::CursorIcon::Help), + egui::CursorIcon::Progress => Some(bevy::window::CursorIcon::Progress), + egui::CursorIcon::Wait => Some(bevy::window::CursorIcon::Wait), + egui::CursorIcon::Cell => Some(bevy::window::CursorIcon::Cell), + egui::CursorIcon::Crosshair => Some(bevy::window::CursorIcon::Crosshair), + egui::CursorIcon::VerticalText => Some(bevy::window::CursorIcon::VerticalText), + egui::CursorIcon::Alias => Some(bevy::window::CursorIcon::Alias), + egui::CursorIcon::Copy => Some(bevy::window::CursorIcon::Copy), + egui::CursorIcon::Move => Some(bevy::window::CursorIcon::Move), + egui::CursorIcon::NoDrop => Some(bevy::window::CursorIcon::NoDrop), + egui::CursorIcon::NotAllowed => Some(bevy::window::CursorIcon::NotAllowed), + egui::CursorIcon::AllScroll => Some(bevy::window::CursorIcon::AllScroll), + egui::CursorIcon::ZoomIn => Some(bevy::window::CursorIcon::ZoomIn), + egui::CursorIcon::ZoomOut => Some(bevy::window::CursorIcon::ZoomOut), + egui::CursorIcon::ResizeEast => Some(bevy::window::CursorIcon::EResize), + egui::CursorIcon::ResizeSouthEast => Some(bevy::window::CursorIcon::SeResize), + egui::CursorIcon::ResizeSouth => Some(bevy::window::CursorIcon::SResize), + egui::CursorIcon::ResizeSouthWest => Some(bevy::window::CursorIcon::SwResize), + egui::CursorIcon::ResizeWest => Some(bevy::window::CursorIcon::WResize), + egui::CursorIcon::ResizeNorthWest => Some(bevy::window::CursorIcon::NwResize), + egui::CursorIcon::ResizeNorth => Some(bevy::window::CursorIcon::NResize), + egui::CursorIcon::ResizeNorthEast => Some(bevy::window::CursorIcon::NeResize), + egui::CursorIcon::ResizeColumn => Some(bevy::window::CursorIcon::ColResize), + egui::CursorIcon::ResizeRow => Some(bevy::window::CursorIcon::RowResize), egui::CursorIcon::None => None, } }