-
Notifications
You must be signed in to change notification settings - Fork 74
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
egui 0.16 #51
Closed
Closed
egui 0.16 #51
Changes from 5 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
00751af
Attempt to update to 0.16
Jengamon 94b58aa
Orphaned TextureAllocator
Jengamon 3f15740
No mutex all over
Jengamon 48959d7
Bump version
Jengamon dd5c559
Edit interface
Jengamon c52eaba
Make some changes as suggested
Jengamon File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,9 @@ use bytemuck::{Pod, Zeroable}; | |
pub use wgpu; | ||
use wgpu::util::DeviceExt; | ||
|
||
use std::collections::HashMap; | ||
use std::sync::Arc; | ||
|
||
pub use {epi, epi::egui}; | ||
|
||
/// Error that the backend can return. | ||
|
@@ -37,9 +40,7 @@ impl std::fmt::Display for BackendError { | |
|
||
impl std::error::Error for BackendError { | ||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { | ||
match *self { | ||
_ => None, | ||
} | ||
None | ||
} | ||
} | ||
|
||
|
@@ -98,8 +99,8 @@ pub struct RenderPass { | |
texture_bind_group: Option<wgpu::BindGroup>, | ||
texture_version: Option<u64>, | ||
next_user_texture_id: u64, | ||
pending_user_textures: Vec<(u64, egui::Texture)>, | ||
user_textures: Vec<Option<wgpu::BindGroup>>, | ||
pending_user_textures: Vec<(u64, UserTexture)>, | ||
user_textures: HashMap<u64, Arc<wgpu::BindGroup>>, | ||
} | ||
|
||
impl RenderPass { | ||
|
@@ -255,7 +256,7 @@ impl RenderPass { | |
texture_bind_group: None, | ||
next_user_texture_id: 0, | ||
pending_user_textures: Vec::new(), | ||
user_textures: Vec::new(), | ||
user_textures: HashMap::new(), | ||
} | ||
} | ||
|
||
|
@@ -264,7 +265,7 @@ impl RenderPass { | |
&self, | ||
encoder: &mut wgpu::CommandEncoder, | ||
color_attachment: &wgpu::TextureView, | ||
paint_jobs: &[egui::paint::ClippedMesh], | ||
paint_jobs: &[egui::epaint::ClippedMesh], | ||
screen_descriptor: &ScreenDescriptor, | ||
clear_color: Option<wgpu::Color>, | ||
) -> Result<(), BackendError> { | ||
|
@@ -299,7 +300,7 @@ impl RenderPass { | |
pub fn execute_with_renderpass<'rpass>( | ||
&'rpass self, | ||
rpass: &mut wgpu::RenderPass<'rpass>, | ||
paint_jobs: &[egui::paint::ClippedMesh], | ||
paint_jobs: &[egui::epaint::ClippedMesh], | ||
screen_descriptor: &ScreenDescriptor, | ||
) -> Result<(), BackendError> { | ||
rpass.set_pipeline(&self.render_pipeline); | ||
|
@@ -369,16 +370,13 @@ impl RenderPass { | |
BackendError::Internal("egui texture was not set before the first draw".to_string()) | ||
})?, | ||
egui::TextureId::User(id) => { | ||
let id = id as usize; | ||
assert!(id < self.user_textures.len()); | ||
&(self | ||
.user_textures | ||
.get(id) | ||
assert!(self.user_textures.contains_key(&id)); | ||
self.user_textures | ||
.get(&id) | ||
.ok_or_else(|| { | ||
BackendError::Internal(format!("user texture {} not found", id)) | ||
})? | ||
.as_ref() | ||
.ok_or_else(|| BackendError::Internal(format!("user texture {} freed", id))))? | ||
} | ||
}; | ||
|
||
|
@@ -390,7 +388,7 @@ impl RenderPass { | |
&mut self, | ||
device: &wgpu::Device, | ||
queue: &wgpu::Queue, | ||
egui_texture: &egui::Texture, | ||
egui_texture: &egui::FontImage, | ||
) { | ||
// Don't update the texture if it hasn't changed. | ||
if self.texture_version == Some(egui_texture.version) { | ||
|
@@ -404,7 +402,7 @@ impl RenderPass { | |
pixels.push(srgba.b()); | ||
pixels.push(srgba.a()); | ||
} | ||
let egui_texture = egui::Texture { | ||
let egui_texture = egui::FontImage { | ||
version: egui_texture.version, | ||
width: egui_texture.width, | ||
height: egui_texture.height, | ||
|
@@ -418,30 +416,34 @@ impl RenderPass { | |
|
||
/// Updates the user textures that the app allocated. Should be called before `execute()`. | ||
pub fn update_user_textures(&mut self, device: &wgpu::Device, queue: &wgpu::Queue) { | ||
let pending_user_textures = std::mem::take(&mut self.pending_user_textures); | ||
for (id, texture) in pending_user_textures { | ||
for (id, texture) in self | ||
.pending_user_textures | ||
.drain(..) | ||
.collect::<Vec<_>>() | ||
.into_iter() | ||
{ | ||
let bind_group = self.egui_texture_to_wgpu( | ||
device, | ||
queue, | ||
&texture, | ||
format!("user_texture{}", id).as_str(), | ||
); | ||
self.user_textures.push(Some(bind_group)); | ||
self.user_textures.insert(id, Arc::new(bind_group)); | ||
} | ||
} | ||
|
||
/// Assumes egui_texture contains srgb data. | ||
/// This does not match how `egui::Texture` is documented as of writing, but this is how it is used for user textures. | ||
fn egui_texture_to_wgpu( | ||
fn egui_texture_to_wgpu<T: UploadableTexture>( | ||
&self, | ||
device: &wgpu::Device, | ||
queue: &wgpu::Queue, | ||
egui_texture: &egui::Texture, | ||
egui_texture: &T, | ||
label: &str, | ||
) -> wgpu::BindGroup { | ||
let size = wgpu::Extent3d { | ||
width: egui_texture.width as u32, | ||
height: egui_texture.height as u32, | ||
width: egui_texture.width() as u32, | ||
height: egui_texture.height() as u32, | ||
depth_or_array_layers: 1, | ||
}; | ||
|
||
|
@@ -462,13 +464,13 @@ impl RenderPass { | |
origin: wgpu::Origin3d::ZERO, | ||
aspect: wgpu::TextureAspect::All, | ||
}, | ||
egui_texture.pixels.as_slice(), | ||
egui_texture.pixel_data(), | ||
wgpu::ImageDataLayout { | ||
offset: 0, | ||
bytes_per_row: NonZeroU32::new( | ||
(egui_texture.pixels.len() / egui_texture.height) as u32, | ||
(egui_texture.pixel_data().len() / egui_texture.height()) as u32, | ||
), | ||
rows_per_image: NonZeroU32::new(egui_texture.height as u32), | ||
rows_per_image: NonZeroU32::new(egui_texture.height() as u32), | ||
}, | ||
size, | ||
); | ||
|
@@ -584,8 +586,9 @@ impl RenderPass { | |
}, | ||
], | ||
}); | ||
let texture_id = egui::TextureId::User(self.next_user_texture_id); | ||
self.user_textures.push(Some(bind_group)); | ||
let id = self.next_user_texture_id; | ||
let texture_id = egui::TextureId::User(id); | ||
self.user_textures.insert(id, Arc::new(bind_group)); | ||
self.next_user_texture_id += 1; | ||
|
||
texture_id | ||
|
@@ -611,7 +614,7 @@ impl RenderPass { | |
} | ||
}; | ||
|
||
let user_texture = self.user_textures.get_mut(id as usize).ok_or_else(|| { | ||
let user_texture = self.user_textures.get_mut(&id).ok_or_else(|| { | ||
BackendError::InvalidTextureId(format!( | ||
"user texture for TextureId {} could not be found", | ||
id | ||
|
@@ -641,7 +644,7 @@ impl RenderPass { | |
], | ||
}); | ||
|
||
*user_texture = Some(bind_group); | ||
*user_texture = Arc::new(bind_group); | ||
|
||
Ok(()) | ||
} | ||
|
@@ -652,7 +655,7 @@ impl RenderPass { | |
&mut self, | ||
device: &wgpu::Device, | ||
queue: &wgpu::Queue, | ||
paint_jobs: &[egui::paint::ClippedMesh], | ||
paint_jobs: &[egui::epaint::ClippedMesh], | ||
screen_descriptor: &ScreenDescriptor, | ||
) { | ||
let index_size = self.index_buffers.len(); | ||
|
@@ -742,27 +745,22 @@ impl RenderPass { | |
queue.write_buffer(&buffer.buffer, 0, data); | ||
} | ||
} | ||
} | ||
|
||
impl epi::TextureAllocator for RenderPass { | ||
fn alloc_srgba_premultiplied( | ||
&mut self, | ||
size: (usize, usize), | ||
srgba_pixels: &[egui::Color32], | ||
) -> egui::TextureId { | ||
let id = self.next_user_texture_id; | ||
self.next_user_texture_id += 1; | ||
// ------- | ||
|
||
/// Creates a texture at a certain user texture location | ||
pub fn set_texture(&mut self, id: u64, image: epi::Image) -> egui::TextureId { | ||
let [width, height] = image.size; | ||
let srgba_pixels = image.pixels; | ||
|
||
let mut pixels = vec![0u8; srgba_pixels.len() * 4]; | ||
for (target, given) in pixels.chunks_exact_mut(4).zip(srgba_pixels.iter()) { | ||
target.copy_from_slice(&given.to_array()); | ||
} | ||
|
||
let (width, height) = size; | ||
self.pending_user_textures.push(( | ||
id, | ||
egui::Texture { | ||
version: 0, | ||
UserTexture { | ||
width, | ||
height, | ||
pixels, | ||
|
@@ -772,14 +770,112 @@ impl epi::TextureAllocator for RenderPass { | |
egui::TextureId::User(id) | ||
} | ||
|
||
fn free(&mut self, id: egui::TextureId) { | ||
/// Frees a user location | ||
pub fn free_texture(&mut self, id: u64) { | ||
self.user_textures.remove(&id); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's also remove this and just offer the other |
||
|
||
/// Frees a texture id (if that texture was a user texture) | ||
pub fn free_texture_id(&mut self, id: egui::TextureId) { | ||
if let egui::TextureId::User(id) = id { | ||
self.user_textures.remove(&id); | ||
} | ||
} | ||
} | ||
|
||
// Describes something that can be uploaded to the GPU as a texture | ||
trait UploadableTexture { | ||
fn width(&self) -> usize; | ||
fn height(&self) -> usize; | ||
fn pixel_data(&self) -> &[u8]; | ||
} | ||
|
||
impl UploadableTexture for egui::FontImage { | ||
fn width(&self) -> usize { | ||
self.width | ||
} | ||
|
||
fn height(&self) -> usize { | ||
self.height | ||
} | ||
|
||
fn pixel_data(&self) -> &[u8] { | ||
&self.pixels | ||
} | ||
} | ||
|
||
struct UserTexture { | ||
width: usize, | ||
height: usize, | ||
pixels: Vec<u8>, | ||
} | ||
|
||
impl UploadableTexture for UserTexture { | ||
fn width(&self) -> usize { | ||
self.width | ||
} | ||
|
||
fn height(&self) -> usize { | ||
self.height | ||
} | ||
|
||
fn pixel_data(&self) -> &[u8] { | ||
&self.pixels | ||
} | ||
} | ||
|
||
impl epi::NativeTexture for RenderPass { | ||
type Texture = Arc<wgpu::BindGroup>; | ||
|
||
fn register_native_texture(&mut self, bg: Self::Texture) -> egui::TextureId { | ||
let id = self.next_user_texture_id; | ||
self.next_user_texture_id += 1; | ||
self.user_textures.insert(id, bg); | ||
egui::TextureId::User(id) | ||
} | ||
|
||
fn replace_native_texture(&mut self, id: egui::TextureId, tex: Self::Texture) { | ||
if let egui::TextureId::User(id) = id { | ||
self.user_textures | ||
.get_mut(id as usize) | ||
.and_then(|option| option.take()); | ||
self.user_textures.insert(id, tex); | ||
} | ||
} | ||
} | ||
// | ||
// impl epi::TextureAllocator for Mutex<RenderPass> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And delete all this commented code. |
||
// fn alloc(&self, image: epi::Image) -> egui::TextureId { | ||
// let id = self.next_user_texture_id.get(); | ||
// self.next_user_texture_id.set(id + 1); | ||
// | ||
// let [width, height] = image.size; | ||
// let srgba_pixels = image.pixels; | ||
// | ||
// let mut pixels = vec![0u8; srgba_pixels.len() * 4]; | ||
// for (target, given) in pixels.chunks_exact_mut(4).zip(srgba_pixels.iter()) { | ||
// target.copy_from_slice(&given.to_array()); | ||
// } | ||
// | ||
// self.pending_user_textures.lock().unwrap().push(( | ||
// id, | ||
// UserTexture { | ||
// width, | ||
// height, | ||
// pixels, | ||
// }, | ||
// )); | ||
// | ||
// egui::TextureId::User(id) | ||
// } | ||
// | ||
// fn free(&self, id: egui::TextureId) { | ||
// if let egui::TextureId::User(id) = id { | ||
// self.user_textures | ||
// .write() | ||
// .unwrap() | ||
// .get_mut(id as usize) | ||
// .and_then(|option| option.take()); | ||
// } | ||
// } | ||
// } | ||
|
||
// Needed since we can't use bytemuck for external types. | ||
fn as_byte_slice<T>(slice: &[T]) -> &[u8] { | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't the
id
be selected for the caller? It's an internal implementation detail. The actual id allocated is already included in the return value, which should be good enough? I can't think of any cases where I would want to specify the id when creating a texture.