1
0
mirror of https://github.com/emilk/egui.git synced 2026-09-02 06:40:06 -04:00

Texture loading in egui (#1110)

* Move texture allocation into epaint/egui proper
* Add TextureHandle
* egui_glow: cast using bytemuck instead of unsafe code
* Optimize glium painter
* Optimize WebGL
* Add example of loading an image from file
This commit is contained in:
Emil Ernerfeldt
2022-01-15 13:59:52 +01:00
committed by GitHub
parent 6c616a1b69
commit 66d80e2519
59 changed files with 1297 additions and 860 deletions

View File

@@ -2,7 +2,6 @@
use std::collections::HashMap;
use bytemuck::cast_slice;
use egui::{
emath::Rect,
epaint::{Color32, Mesh, Vertex},
@@ -11,7 +10,7 @@ use glow::HasContext;
use memoffset::offset_of;
use crate::misc_util::{
as_u8_slice, check_for_gl_error, compile_shader, glow_print, link_program, srgb_texture2d,
check_for_gl_error, compile_shader, glow_print, link_program, srgb_texture2d,
};
use crate::post_process::PostProcess;
use crate::shader_version::ShaderVersion;
@@ -30,8 +29,6 @@ pub struct Painter {
program: glow::Program,
u_screen_size: glow::UniformLocation,
u_sampler: glow::UniformLocation,
egui_texture: Option<glow::Texture>,
egui_texture_version: Option<u64>,
is_webgl_1: bool,
is_embedded: bool,
vertex_array: crate::misc_util::VAO,
@@ -42,8 +39,7 @@ pub struct Painter {
vertex_buffer: glow::Buffer,
element_array_buffer: glow::Buffer,
/// Index is the same as in [`egui::TextureId::User`].
user_textures: HashMap<u64, glow::Texture>,
textures: HashMap<egui::TextureId, glow::Texture>,
#[cfg(feature = "epi")]
next_native_tex_id: u64, // TODO: 128-bit texture space?
@@ -212,8 +208,6 @@ impl Painter {
program,
u_screen_size,
u_sampler,
egui_texture: None,
egui_texture_version: None,
is_webgl_1,
is_embedded: matches!(shader_version, ShaderVersion::Es100 | ShaderVersion::Es300),
vertex_array,
@@ -222,7 +216,7 @@ impl Painter {
post_process,
vertex_buffer,
element_array_buffer,
user_textures: Default::default(),
textures: Default::default(),
#[cfg(feature = "epi")]
next_native_tex_id: 1 << 32,
textures_to_destroy: Vec::new(),
@@ -231,41 +225,6 @@ impl Painter {
}
}
pub fn upload_egui_texture(&mut self, gl: &glow::Context, font_image: &egui::FontImage) {
self.assert_not_destroyed();
if self.egui_texture_version == Some(font_image.version) {
return; // No change
}
let gamma = if self.is_embedded && self.post_process.is_none() {
1.0 / 2.2
} else {
1.0
};
let pixels: Vec<u8> = font_image
.srgba_pixels(gamma)
.flat_map(|a| Vec::from(a.to_array()))
.collect();
if let Some(old_tex) = std::mem::replace(
&mut self.egui_texture,
Some(srgb_texture2d(
gl,
self.is_webgl_1,
self.srgb_support,
self.texture_filter,
&pixels,
font_image.width,
font_image.height,
)),
) {
unsafe {
gl.delete_texture(old_tex);
}
}
self.egui_texture_version = Some(font_image.version);
}
unsafe fn prepare_painting(
&mut self,
[width_in_pixels, height_in_pixels]: [u32; 2],
@@ -370,14 +329,14 @@ impl Painter {
gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vertex_buffer));
gl.buffer_data_u8_slice(
glow::ARRAY_BUFFER,
as_u8_slice(mesh.vertices.as_slice()),
bytemuck::cast_slice(&mesh.vertices),
glow::STREAM_DRAW,
);
gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(self.element_array_buffer));
gl.buffer_data_u8_slice(
glow::ELEMENT_ARRAY_BUFFER,
as_u8_slice(mesh.indices.as_slice()),
bytemuck::cast_slice(&mesh.indices),
glow::STREAM_DRAW,
);
@@ -425,52 +384,75 @@ impl Painter {
// ------------------------------------------------------------------------
#[cfg(feature = "epi")]
pub fn set_texture(&mut self, gl: &glow::Context, tex_id: u64, image: &epi::Image) {
pub fn set_texture(
&mut self,
gl: &glow::Context,
tex_id: egui::TextureId,
image: &egui::ImageData,
) {
self.assert_not_destroyed();
assert_eq!(
image.size[0] * image.size[1],
image.pixels.len(),
"Mismatch between texture size and texel count"
);
let gl_texture = match image {
egui::ImageData::Color(image) => {
assert_eq!(
image.width() * image.height(),
image.pixels.len(),
"Mismatch between texture size and texel count"
);
let data: &[u8] = cast_slice(image.pixels.as_ref());
let data: &[u8] = bytemuck::cast_slice(image.pixels.as_ref());
let gl_texture = srgb_texture2d(
gl,
self.is_webgl_1,
self.srgb_support,
self.texture_filter,
data,
image.size[0],
image.size[1],
);
srgb_texture2d(
gl,
self.is_webgl_1,
self.srgb_support,
self.texture_filter,
data,
image.size[0],
image.size[1],
)
}
egui::ImageData::Alpha(image) => {
let gamma = if self.is_embedded && self.post_process.is_none() {
1.0 / 2.2
} else {
1.0
};
let data: Vec<u8> = image
.srgba_pixels(gamma)
.flat_map(|a| a.to_array())
.collect();
if let Some(old_tex) = self.user_textures.insert(tex_id, gl_texture) {
self.textures_to_destroy.push(old_tex);
srgb_texture2d(
gl,
self.is_webgl_1,
self.srgb_support,
self.texture_filter,
&data,
image.size[0],
image.size[1],
)
}
};
if let Some(old_tex) = self.textures.insert(tex_id, gl_texture) {
unsafe { gl.delete_texture(old_tex) };
}
}
pub fn free_texture(&mut self, tex_id: u64) {
self.user_textures.remove(&tex_id);
pub fn free_texture(&mut self, gl: &glow::Context, tex_id: egui::TextureId) {
if let Some(old_tex) = self.textures.remove(&tex_id) {
unsafe { gl.delete_texture(old_tex) };
}
}
fn get_texture(&self, texture_id: egui::TextureId) -> Option<glow::Texture> {
self.assert_not_destroyed();
match texture_id {
egui::TextureId::Egui => self.egui_texture,
egui::TextureId::User(id) => self.user_textures.get(&id).copied(),
}
self.textures.get(&texture_id).copied()
}
unsafe fn destroy_gl(&self, gl: &glow::Context) {
gl.delete_program(self.program);
if let Some(tex) = self.egui_texture {
gl.delete_texture(tex);
}
for tex in self.user_textures.values() {
for tex in self.textures.values() {
gl.delete_texture(*tex);
}
gl.delete_buffer(self.vertex_buffer);
@@ -533,20 +515,15 @@ impl epi::NativeTexture for Painter {
fn register_native_texture(&mut self, native: Self::Texture) -> egui::TextureId {
self.assert_not_destroyed();
let id = self.next_native_tex_id;
let id = egui::TextureId::User(self.next_native_tex_id);
self.next_native_tex_id += 1;
self.user_textures.insert(id, native);
egui::TextureId::User(id as u64)
self.textures.insert(id, native);
id
}
fn replace_native_texture(&mut self, id: egui::TextureId, replacing: Self::Texture) {
if let egui::TextureId::User(id) = id {
if let Some(old_tex) = self.user_textures.insert(id, replacing) {
self.textures_to_destroy.push(old_tex);
}
if let Some(old_tex) = self.textures.insert(id, replacing) {
self.textures_to_destroy.push(old_tex);
}
}
}