2022-09-21 19:30:02 +00:00
#![ allow(clippy::collapsible_else_if) ]
2021-10-18 21:13:32 +00:00
#![ allow(unsafe_code) ]
2022-05-22 15:43:30 +00:00
use std ::{ collections ::HashMap , sync ::Arc } ;
2021-12-26 20:21:28 +00:00
2021-10-18 21:13:32 +00:00
use egui ::{
emath ::Rect ,
2023-02-04 10:02:15 +00:00
epaint ::{ Mesh , PaintCallbackInfo , Primitive , Vertex } ,
2021-10-18 21:13:32 +00:00
} ;
2022-03-22 15:04:06 +00:00
use glow ::HasContext as _ ;
2021-12-26 20:21:28 +00:00
use memoffset ::offset_of ;
2021-10-18 21:13:32 +00:00
2022-03-22 15:04:06 +00:00
use crate ::check_for_gl_error ;
use crate ::misc_util ::{ compile_shader , link_program } ;
2021-11-03 18:17:07 +00:00
use crate ::shader_version ::ShaderVersion ;
2022-03-22 15:04:06 +00:00
use crate ::vao ;
2021-10-18 21:13:32 +00:00
2021-12-26 20:21:28 +00:00
pub use glow ::Context ;
2021-10-18 21:13:32 +00:00
const VERT_SRC : & str = include_str! ( " shader/vertex.glsl " ) ;
const FRAG_SRC : & str = include_str! ( " shader/fragment.glsl " ) ;
2022-05-22 14:56:51 +00:00
trait TextureFilterExt {
fn glow_code ( & self ) -> u32 ;
2022-04-30 10:58:29 +00:00
}
2022-08-02 15:26:33 +00:00
2022-11-02 16:54:06 +00:00
impl TextureFilterExt for egui ::TextureFilter {
2022-05-22 14:56:51 +00:00
fn glow_code ( & self ) -> u32 {
2022-04-30 10:58:29 +00:00
match self {
2022-11-02 16:54:06 +00:00
egui ::TextureFilter ::Linear = > glow ::LINEAR ,
egui ::TextureFilter ::Nearest = > glow ::NEAREST ,
2022-04-30 10:58:29 +00:00
}
}
}
2022-03-14 12:25:11 +00:00
/// An OpenGL painter using [`glow`].
///
/// This is responsible for painting egui and managing egui textures.
/// You can access the underlying [`glow::Context`] with [`Self::gl`].
2021-10-18 21:13:32 +00:00
///
/// This struct must be destroyed with [`Painter::destroy`] before dropping, to ensure OpenGL
/// objects have been properly deleted and are not leaked.
pub struct Painter {
2022-05-22 15:43:30 +00:00
gl : Arc < glow ::Context > ,
2022-03-14 12:25:11 +00:00
2022-01-24 13:32:36 +00:00
max_texture_side : usize ,
2021-11-03 18:17:07 +00:00
program : glow ::Program ,
2021-10-18 21:13:32 +00:00
u_screen_size : glow ::UniformLocation ,
u_sampler : glow ::UniformLocation ,
2021-11-03 18:17:07 +00:00
is_webgl_1 : bool ,
2022-03-22 22:11:27 +00:00
vao : crate ::vao ::VertexArrayObject ,
2022-09-21 19:30:02 +00:00
srgb_textures : bool ,
2022-03-22 22:11:27 +00:00
vbo : glow ::Buffer ,
2021-11-03 18:17:07 +00:00
element_array_buffer : glow ::Buffer ,
2021-10-18 21:13:32 +00:00
2022-01-15 12:59:52 +00:00
textures : HashMap < egui ::TextureId , glow ::Texture > ,
2021-12-28 12:22:01 +00:00
2022-05-21 14:53:25 +00:00
next_native_tex_id : u64 ,
2021-12-28 12:22:01 +00:00
2021-12-26 20:21:28 +00:00
/// Stores outdated OpenGL textures that are yet to be deleted
textures_to_destroy : Vec < glow ::Texture > ,
2021-10-18 21:13:32 +00:00
2021-12-28 13:24:59 +00:00
/// Used to make sure we are destroyed correctly.
2021-12-26 20:21:28 +00:00
destroyed : bool ,
2021-10-18 21:13:32 +00:00
}
2022-05-28 15:52:36 +00:00
/// A callback function that can be used to compose an [`egui::PaintCallback`] for custom rendering
/// with [`glow`].
///
/// The callback is passed, the [`egui::PaintCallbackInfo`] and the [`Painter`] which can be used to
/// access the OpenGL context.
///
/// # Example
///
2022-08-20 13:18:02 +00:00
/// See the [`custom3d_glow`](https://github.com/emilk/egui/blob/master/crates/egui_demo_app/src/apps/custom3d_wgpu.rs) demo source for a detailed usage example.
2022-05-28 15:52:36 +00:00
pub struct CallbackFn {
f : Box < dyn Fn ( PaintCallbackInfo , & Painter ) + Sync + Send > ,
}
impl CallbackFn {
pub fn new < F : Fn ( PaintCallbackInfo , & Painter ) + Sync + Send + 'static > ( callback : F ) -> Self {
let f = Box ::new ( callback ) ;
CallbackFn { f }
}
}
2021-10-18 21:13:32 +00:00
impl Painter {
2021-11-03 18:17:07 +00:00
/// Create painter.
///
/// Set `pp_fb_extent` to the framebuffer size to enable `sRGB` support on OpenGL ES and WebGL.
2021-11-13 11:32:01 +00:00
///
2021-11-13 11:55:48 +00:00
/// Set `shader_prefix` if you want to turn on shader workaround e.g. `"#define APPLY_BRIGHTENING_GAMMA\n"`
/// (see <https://github.com/emilk/egui/issues/794>).
///
2021-11-03 18:17:07 +00:00
/// # Errors
/// will return `Err` below cases
/// * failed to compile shader
/// * failed to create postprocess on webgl with `sRGB` support
/// * failed to create buffer
2021-11-13 11:32:01 +00:00
pub fn new (
2022-05-22 15:43:30 +00:00
gl : Arc < glow ::Context > ,
2021-11-13 11:32:01 +00:00
shader_prefix : & str ,
2022-09-06 08:08:16 +00:00
shader_version : Option < ShaderVersion > ,
2021-11-13 11:32:01 +00:00
) -> Result < Painter , String > {
2022-04-13 09:06:13 +00:00
crate ::profile_function! ( ) ;
2022-05-05 21:17:33 +00:00
crate ::check_for_gl_error_even_in_release! ( & gl , " before Painter::new " ) ;
2022-01-09 22:04:00 +00:00
2023-02-08 13:28:42 +00:00
// some useful debug info. all three of them are present in gl 1.1.
unsafe {
let version = gl . get_parameter_string ( glow ::VERSION ) ;
let renderer = gl . get_parameter_string ( glow ::RENDERER ) ;
let vendor = gl . get_parameter_string ( glow ::VENDOR ) ;
tracing ::debug! (
" \n opengl version: {version} \n opengl renderer: {renderer} \n opengl vendor: {vendor} "
) ;
}
#[ cfg(not(target_arch = " wasm32 " )) ]
if gl . version ( ) . major < 2 {
// this checks on desktop that we are not using opengl 1.1 microsoft sw rendering context.
// ShaderVersion::get fn will segfault due to SHADING_LANGUAGE_VERSION (added in gl2.0)
return Err ( " egui_glow requires opengl 2.0+. " . to_owned ( ) ) ;
}
2022-01-24 13:32:36 +00:00
let max_texture_side = unsafe { gl . get_parameter_i32 ( glow ::MAX_TEXTURE_SIZE ) } as usize ;
2022-09-21 19:30:02 +00:00
let shader_version = shader_version . unwrap_or_else ( | | ShaderVersion ::get ( & gl ) ) ;
let is_webgl_1 = shader_version = = ShaderVersion ::Es100 ;
2022-09-24 15:53:11 +00:00
let shader_version_declaration = shader_version . version_declaration ( ) ;
tracing ::debug! ( " Shader header: {:?}. " , shader_version_declaration ) ;
2022-01-10 22:19:30 +00:00
2022-09-21 19:30:02 +00:00
let supported_extensions = gl . supported_extensions ( ) ;
tracing ::trace! ( " OpenGL extensions: {supported_extensions:?} " ) ;
let srgb_textures = shader_version = = ShaderVersion ::Es300 // WebGL2 always support sRGB
| | supported_extensions . iter ( ) . any ( | extension | {
// EXT_sRGB, GL_ARB_framebuffer_sRGB, GL_EXT_sRGB, GL_EXT_texture_sRGB_decode, …
extension . contains ( " sRGB " )
} ) ;
2022-09-24 15:53:11 +00:00
tracing ::debug! ( " SRGB texture Support: {:?} " , srgb_textures ) ;
2021-10-20 10:29:35 +00:00
2021-10-18 21:13:32 +00:00
unsafe {
2021-11-03 18:17:07 +00:00
let vert = compile_shader (
2022-03-14 12:25:11 +00:00
& gl ,
2021-11-03 18:17:07 +00:00
glow ::VERTEX_SHADER ,
& format! (
2022-09-24 15:53:11 +00:00
" {} \n #define NEW_SHADER_INTERFACE {} \n {} \n {} " ,
shader_version_declaration ,
shader_version . is_new_shader_interface ( ) as i32 ,
2021-11-13 11:32:01 +00:00
shader_prefix ,
2021-11-03 18:17:07 +00:00
VERT_SRC
) ,
) ? ;
let frag = compile_shader (
2022-03-14 12:25:11 +00:00
& gl ,
2021-11-03 18:17:07 +00:00
glow ::FRAGMENT_SHADER ,
& format! (
2022-09-24 15:53:11 +00:00
" {} \n #define NEW_SHADER_INTERFACE {} \n #define SRGB_TEXTURES {} \n {} \n {} " ,
shader_version_declaration ,
shader_version . is_new_shader_interface ( ) as i32 ,
srgb_textures as i32 ,
2021-11-13 11:32:01 +00:00
shader_prefix ,
2021-11-03 18:17:07 +00:00
FRAG_SRC
) ,
) ? ;
2022-03-14 12:25:11 +00:00
let program = link_program ( & gl , [ vert , frag ] . iter ( ) ) ? ;
2021-10-20 10:29:35 +00:00
gl . detach_shader ( program , vert ) ;
gl . detach_shader ( program , frag ) ;
gl . delete_shader ( vert ) ;
gl . delete_shader ( frag ) ;
2021-10-18 21:13:32 +00:00
let u_screen_size = gl . get_uniform_location ( program , " u_screen_size " ) . unwrap ( ) ;
let u_sampler = gl . get_uniform_location ( program , " u_sampler " ) . unwrap ( ) ;
2022-03-22 22:11:27 +00:00
let vbo = gl . create_buffer ( ) ? ;
2021-10-18 21:13:32 +00:00
let a_pos_loc = gl . get_attrib_location ( program , " a_pos " ) . unwrap ( ) ;
let a_tc_loc = gl . get_attrib_location ( program , " a_tc " ) . unwrap ( ) ;
let a_srgba_loc = gl . get_attrib_location ( program , " a_srgba " ) . unwrap ( ) ;
2022-03-22 22:11:27 +00:00
2021-11-03 18:17:07 +00:00
let stride = std ::mem ::size_of ::< Vertex > ( ) as i32 ;
2022-03-22 22:11:27 +00:00
let buffer_infos = vec! [
vao ::BufferInfo {
location : a_pos_loc ,
vector_size : 2 ,
data_type : glow ::FLOAT ,
normalized : false ,
stride ,
offset : offset_of ! ( Vertex , pos ) as i32 ,
} ,
vao ::BufferInfo {
location : a_tc_loc ,
vector_size : 2 ,
data_type : glow ::FLOAT ,
normalized : false ,
stride ,
offset : offset_of ! ( Vertex , uv ) as i32 ,
} ,
vao ::BufferInfo {
location : a_srgba_loc ,
vector_size : 4 ,
data_type : glow ::UNSIGNED_BYTE ,
normalized : false ,
stride ,
offset : offset_of ! ( Vertex , color ) as i32 ,
} ,
] ;
let vao = crate ::vao ::VertexArrayObject ::new ( & gl , vbo , buffer_infos ) ;
let element_array_buffer = gl . create_buffer ( ) ? ;
2022-05-05 21:17:33 +00:00
crate ::check_for_gl_error_even_in_release! ( & gl , " after Painter::new " ) ;
2021-10-18 21:13:32 +00:00
2021-11-03 18:17:07 +00:00
Ok ( Painter {
2022-03-14 12:25:11 +00:00
gl ,
2022-01-24 13:32:36 +00:00
max_texture_side ,
2021-10-18 21:13:32 +00:00
program ,
u_screen_size ,
u_sampler ,
2021-11-03 18:17:07 +00:00
is_webgl_1 ,
2022-03-22 22:11:27 +00:00
vao ,
2022-09-21 19:30:02 +00:00
srgb_textures ,
2022-03-22 22:11:27 +00:00
vbo ,
2021-10-18 21:13:32 +00:00
element_array_buffer ,
2022-01-15 12:59:52 +00:00
textures : Default ::default ( ) ,
2021-12-26 20:21:28 +00:00
next_native_tex_id : 1 < < 32 ,
textures_to_destroy : Vec ::new ( ) ,
2021-10-18 21:13:32 +00:00
destroyed : false ,
2021-11-03 18:17:07 +00:00
} )
2021-10-18 21:13:32 +00:00
}
}
2022-03-14 12:25:11 +00:00
/// Access the shared glow context.
2022-05-22 15:43:30 +00:00
pub fn gl ( & self ) -> & Arc < glow ::Context > {
2022-03-14 12:25:11 +00:00
& self . gl
}
2022-01-24 13:32:36 +00:00
pub fn max_texture_side ( & self ) -> usize {
self . max_texture_side
}
2022-08-03 07:38:46 +00:00
/// The framebuffer we use as an intermediate render target,
/// or `None` if we are painting to the screen framebuffer directly.
///
/// This is the framebuffer that is bound when [`egui::Shape::Callback`] is called,
/// and is where any callbacks should ultimately render onto.
///
/// So if in a [`egui::Shape::Callback`] you need to use an offscreen FBO, you should
/// then restore to this afterwards with
/// `gl.bind_framebuffer(glow::FRAMEBUFFER, painter.intermediate_fbo());`
2022-09-24 15:53:11 +00:00
#[ allow(clippy::unused_self) ]
2022-08-03 07:38:46 +00:00
pub fn intermediate_fbo ( & self ) -> Option < glow ::Framebuffer > {
2022-09-24 15:53:11 +00:00
// We don't currently ever render to an offscreen buffer,
// but we may want to start to in order to do anti-aliasing on web, for instance.
None
2022-08-03 07:38:46 +00:00
}
2021-10-18 21:13:32 +00:00
unsafe fn prepare_painting (
& mut self ,
2021-11-03 18:17:07 +00:00
[ width_in_pixels , height_in_pixels ] : [ u32 ; 2 ] ,
2021-10-18 21:13:32 +00:00
pixels_per_point : f32 ,
) -> ( u32 , u32 ) {
2022-03-14 12:25:11 +00:00
self . gl . enable ( glow ::SCISSOR_TEST ) ;
2021-10-20 10:29:35 +00:00
// egui outputs mesh in both winding orders
2022-03-14 12:25:11 +00:00
self . gl . disable ( glow ::CULL_FACE ) ;
2022-03-22 22:11:27 +00:00
self . gl . disable ( glow ::DEPTH_TEST ) ;
self . gl . color_mask ( true , true , true , true ) ;
2021-10-18 21:13:32 +00:00
2022-03-14 12:25:11 +00:00
self . gl . enable ( glow ::BLEND ) ;
2022-03-22 22:11:27 +00:00
self . gl
. blend_equation_separate ( glow ::FUNC_ADD , glow ::FUNC_ADD ) ;
2022-03-14 12:25:11 +00:00
self . gl . blend_func_separate (
2021-10-18 21:13:32 +00:00
// egui outputs colors with premultiplied alpha:
glow ::ONE ,
glow ::ONE_MINUS_SRC_ALPHA ,
// Less important, but this is technically the correct alpha blend function
// when you want to make use of the framebuffer alpha (for screenshots, compositing, etc).
glow ::ONE_MINUS_DST_ALPHA ,
glow ::ONE ,
) ;
2022-03-20 22:04:44 +00:00
if ! cfg! ( target_arch = " wasm32 " ) {
2022-09-24 15:53:11 +00:00
self . gl . disable ( glow ::FRAMEBUFFER_SRGB ) ;
2022-03-22 15:04:06 +00:00
check_for_gl_error! ( & self . gl , " FRAMEBUFFER_SRGB " ) ;
2022-03-20 22:04:44 +00:00
}
2021-10-18 21:13:32 +00:00
let width_in_points = width_in_pixels as f32 / pixels_per_point ;
let height_in_points = height_in_pixels as f32 / pixels_per_point ;
2022-03-14 12:25:11 +00:00
self . gl
. viewport ( 0 , 0 , width_in_pixels as i32 , height_in_pixels as i32 ) ;
self . gl . use_program ( Some ( self . program ) ) ;
2021-10-18 21:13:32 +00:00
2022-03-14 12:25:11 +00:00
self . gl
. uniform_2_f32 ( Some ( & self . u_screen_size ) , width_in_points , height_in_points ) ;
self . gl . uniform_1_i32 ( Some ( & self . u_sampler ) , 0 ) ;
self . gl . active_texture ( glow ::TEXTURE0 ) ;
2021-10-18 21:13:32 +00:00
2022-03-22 22:11:27 +00:00
self . vao . bind ( & self . gl ) ;
2022-03-14 12:25:11 +00:00
self . gl
. bind_buffer ( glow ::ELEMENT_ARRAY_BUFFER , Some ( self . element_array_buffer ) ) ;
2021-10-18 21:13:32 +00:00
2022-03-22 15:04:06 +00:00
check_for_gl_error! ( & self . gl , " prepare_painting " ) ;
2021-10-18 21:13:32 +00:00
( width_in_pixels , height_in_pixels )
}
2022-05-21 14:53:05 +00:00
/// You are expected to have cleared the color buffer before calling this.
2022-02-21 20:49:52 +00:00
pub fn paint_and_update_textures (
& mut self ,
2022-05-21 14:03:26 +00:00
screen_size_px : [ u32 ; 2 ] ,
2022-02-21 20:49:52 +00:00
pixels_per_point : f32 ,
2022-03-14 12:25:11 +00:00
clipped_primitives : & [ egui ::ClippedPrimitive ] ,
2022-02-21 20:49:52 +00:00
textures_delta : & egui ::TexturesDelta ,
) {
2022-04-13 09:06:13 +00:00
crate ::profile_function! ( ) ;
2022-02-21 20:49:52 +00:00
for ( id , image_delta ) in & textures_delta . set {
2022-03-14 12:25:11 +00:00
self . set_texture ( * id , image_delta ) ;
2022-02-21 20:49:52 +00:00
}
2022-05-21 14:03:26 +00:00
self . paint_primitives ( screen_size_px , pixels_per_point , clipped_primitives ) ;
2022-02-21 20:49:52 +00:00
for & id in & textures_delta . free {
2022-03-14 12:25:11 +00:00
self . free_texture ( id ) ;
2022-02-21 20:49:52 +00:00
}
}
2021-10-18 21:13:32 +00:00
/// Main entry-point for painting a frame.
2022-05-21 14:53:05 +00:00
///
2021-10-18 21:13:32 +00:00
/// You should call `target.clear_color(..)` before
/// and `target.finish()` after this.
///
/// The following OpenGL features will be set:
/// - Scissor test will be enabled
/// - Cull face will be disabled
/// - Blend will be enabled
///
/// The scissor area and blend parameters will be changed.
///
2021-11-03 18:17:07 +00:00
/// As well as this, the following objects will be unset:
2021-10-18 21:13:32 +00:00
/// - Vertex Buffer
/// - Element Buffer
/// - Texture (and active texture will be set to 0)
/// - Program
///
/// Please be mindful of these effects when integrating into your program, and also be mindful
/// of the effects your program might have on this code. Look at the source if in doubt.
2022-03-14 12:25:11 +00:00
pub fn paint_primitives (
2021-10-18 21:13:32 +00:00
& mut self ,
2022-05-21 14:03:26 +00:00
screen_size_px : [ u32 ; 2 ] ,
2021-10-18 21:13:32 +00:00
pixels_per_point : f32 ,
2022-03-14 12:25:11 +00:00
clipped_primitives : & [ egui ::ClippedPrimitive ] ,
2021-10-18 21:13:32 +00:00
) {
2022-04-13 09:06:13 +00:00
crate ::profile_function! ( ) ;
2021-10-18 21:13:32 +00:00
self . assert_not_destroyed ( ) ;
2022-05-21 14:03:26 +00:00
let size_in_pixels = unsafe { self . prepare_painting ( screen_size_px , pixels_per_point ) } ;
2022-03-14 12:25:11 +00:00
for egui ::ClippedPrimitive {
clip_rect ,
primitive ,
} in clipped_primitives
{
set_clip_rect ( & self . gl , size_in_pixels , pixels_per_point , * clip_rect ) ;
match primitive {
Primitive ::Mesh ( mesh ) = > {
self . paint_mesh ( mesh ) ;
}
Primitive ::Callback ( callback ) = > {
if callback . rect . is_positive ( ) {
2022-04-13 09:06:13 +00:00
crate ::profile_scope! ( " callback " ) ;
2022-03-14 12:25:11 +00:00
// Transform callback rect to physical pixels:
let rect_min_x = pixels_per_point * callback . rect . min . x ;
let rect_min_y = pixels_per_point * callback . rect . min . y ;
let rect_max_x = pixels_per_point * callback . rect . max . x ;
let rect_max_y = pixels_per_point * callback . rect . max . y ;
let rect_min_x = rect_min_x . round ( ) as i32 ;
let rect_min_y = rect_min_y . round ( ) as i32 ;
let rect_max_x = rect_max_x . round ( ) as i32 ;
let rect_max_y = rect_max_y . round ( ) as i32 ;
unsafe {
self . gl . viewport (
rect_min_x ,
size_in_pixels . 1 as i32 - rect_max_y ,
rect_max_x - rect_min_x ,
rect_max_y - rect_min_y ,
) ;
}
2022-03-22 22:11:27 +00:00
let info = egui ::PaintCallbackInfo {
2022-04-15 08:31:33 +00:00
viewport : callback . rect ,
clip_rect : * clip_rect ,
2022-03-22 22:11:27 +00:00
pixels_per_point ,
2022-05-21 14:03:26 +00:00
screen_size_px ,
2022-03-22 22:11:27 +00:00
} ;
2022-05-28 15:52:36 +00:00
if let Some ( callback ) = callback . callback . downcast_ref ::< CallbackFn > ( ) {
( callback . f ) ( info , self ) ;
} else {
tracing ::warn! ( " Warning: Unsupported render callback. Expected egui_glow::CallbackFn " ) ;
}
2022-03-14 12:25:11 +00:00
2022-03-22 15:04:06 +00:00
check_for_gl_error! ( & self . gl , " callback " ) ;
2022-03-14 12:25:11 +00:00
// Restore state:
2022-09-24 15:53:11 +00:00
unsafe { self . prepare_painting ( screen_size_px , pixels_per_point ) } ;
2022-03-14 12:25:11 +00:00
}
}
}
2021-10-18 21:13:32 +00:00
}
2022-03-22 22:11:27 +00:00
2021-11-03 18:17:07 +00:00
unsafe {
2022-03-22 22:11:27 +00:00
self . vao . unbind ( & self . gl ) ;
2022-03-14 12:25:11 +00:00
self . gl . bind_buffer ( glow ::ELEMENT_ARRAY_BUFFER , None ) ;
2021-10-18 21:13:32 +00:00
2022-03-14 12:25:11 +00:00
self . gl . disable ( glow ::SCISSOR_TEST ) ;
2021-11-27 10:44:23 +00:00
2022-03-22 15:04:06 +00:00
check_for_gl_error! ( & self . gl , " painting " ) ;
2021-11-03 18:17:07 +00:00
}
2021-10-18 21:13:32 +00:00
}
#[ inline(never) ] // Easier profiling
2022-03-14 12:25:11 +00:00
fn paint_mesh ( & mut self , mesh : & Mesh ) {
2021-10-18 21:13:32 +00:00
debug_assert! ( mesh . is_valid ( ) ) ;
2022-08-08 10:21:53 +00:00
if let Some ( texture ) = self . texture ( mesh . texture_id ) {
2021-10-18 21:13:32 +00:00
unsafe {
2022-03-22 22:11:27 +00:00
self . gl . bind_buffer ( glow ::ARRAY_BUFFER , Some ( self . vbo ) ) ;
2022-03-14 12:25:11 +00:00
self . gl . buffer_data_u8_slice (
2021-10-18 21:13:32 +00:00
glow ::ARRAY_BUFFER ,
2022-01-15 12:59:52 +00:00
bytemuck ::cast_slice ( & mesh . vertices ) ,
2021-10-18 21:13:32 +00:00
glow ::STREAM_DRAW ,
) ;
2022-03-14 12:25:11 +00:00
self . gl
. bind_buffer ( glow ::ELEMENT_ARRAY_BUFFER , Some ( self . element_array_buffer ) ) ;
self . gl . buffer_data_u8_slice (
2021-10-18 21:13:32 +00:00
glow ::ELEMENT_ARRAY_BUFFER ,
2022-01-15 12:59:52 +00:00
bytemuck ::cast_slice ( & mesh . indices ) ,
2021-10-18 21:13:32 +00:00
glow ::STREAM_DRAW ,
) ;
2022-03-14 12:25:11 +00:00
self . gl . bind_texture ( glow ::TEXTURE_2D , Some ( texture ) ) ;
2021-10-18 21:13:32 +00:00
}
2022-01-22 10:23:12 +00:00
2021-10-18 21:13:32 +00:00
unsafe {
2022-03-14 12:25:11 +00:00
self . gl . draw_elements (
2021-10-18 21:13:32 +00:00
glow ::TRIANGLES ,
mesh . indices . len ( ) as i32 ,
glow ::UNSIGNED_INT ,
0 ,
) ;
}
2022-03-22 15:04:06 +00:00
check_for_gl_error! ( & self . gl , " paint_mesh " ) ;
2022-07-29 14:07:26 +00:00
} else {
tracing ::warn! ( " Failed to find texture {:?} " , mesh . texture_id ) ;
2021-10-18 21:13:32 +00:00
}
}
// ------------------------------------------------------------------------
2022-03-14 12:25:11 +00:00
pub fn set_texture ( & mut self , tex_id : egui ::TextureId , delta : & egui ::epaint ::ImageDelta ) {
2022-04-13 09:06:13 +00:00
crate ::profile_function! ( ) ;
2021-10-18 21:13:32 +00:00
self . assert_not_destroyed ( ) ;
2022-01-21 18:51:52 +00:00
let glow_texture = * self
. textures
. entry ( tex_id )
2022-03-14 12:25:11 +00:00
. or_insert_with ( | | unsafe { self . gl . create_texture ( ) . unwrap ( ) } ) ;
2022-01-21 18:51:52 +00:00
unsafe {
2022-03-14 12:25:11 +00:00
self . gl . bind_texture ( glow ::TEXTURE_2D , Some ( glow_texture ) ) ;
2022-01-21 18:51:52 +00:00
}
2022-01-22 10:23:12 +00:00
match & delta . image {
2022-01-15 12:59:52 +00:00
egui ::ImageData ::Color ( image ) = > {
assert_eq! (
image . width ( ) * image . height ( ) ,
image . pixels . len ( ) ,
" Mismatch between texture size and texel count "
) ;
2021-10-18 21:13:32 +00:00
2022-01-15 12:59:52 +00:00
let data : & [ u8 ] = bytemuck ::cast_slice ( image . pixels . as_ref ( ) ) ;
2022-11-02 16:54:06 +00:00
self . upload_texture_srgb ( delta . pos , image . size , delta . options , data ) ;
2022-01-15 12:59:52 +00:00
}
2022-03-23 15:49:49 +00:00
egui ::ImageData ::Font ( image ) = > {
2022-01-21 18:51:52 +00:00
assert_eq! (
image . width ( ) * image . height ( ) ,
image . pixels . len ( ) ,
" Mismatch between texture size and texel count "
) ;
2022-01-15 12:59:52 +00:00
let data : Vec < u8 > = image
2022-09-24 15:53:11 +00:00
. srgba_pixels ( None )
2022-01-15 12:59:52 +00:00
. flat_map ( | a | a . to_array ( ) )
. collect ( ) ;
2022-11-02 16:54:06 +00:00
self . upload_texture_srgb ( delta . pos , image . size , delta . options , & data ) ;
2022-01-15 12:59:52 +00:00
}
} ;
2022-01-21 18:51:52 +00:00
}
2021-10-18 21:13:32 +00:00
2022-05-22 14:56:51 +00:00
fn upload_texture_srgb (
& mut self ,
pos : Option < [ usize ; 2 ] > ,
[ w , h ] : [ usize ; 2 ] ,
2022-11-02 16:54:06 +00:00
options : egui ::TextureOptions ,
2022-05-22 14:56:51 +00:00
data : & [ u8 ] ,
) {
2022-01-21 18:51:52 +00:00
assert_eq! ( data . len ( ) , w * h * 4 ) ;
2022-03-19 12:30:29 +00:00
assert! (
w < = self . max_texture_side & & h < = self . max_texture_side ,
" Got a texture image of size {}x{}, but the maximum supported texture side is only {} " ,
w ,
h ,
self . max_texture_side
) ;
2022-01-21 18:51:52 +00:00
unsafe {
2022-03-14 12:25:11 +00:00
self . gl . tex_parameter_i32 (
2022-01-21 18:51:52 +00:00
glow ::TEXTURE_2D ,
glow ::TEXTURE_MAG_FILTER ,
2022-11-02 16:54:06 +00:00
options . magnification . glow_code ( ) as i32 ,
2022-01-21 18:51:52 +00:00
) ;
2022-03-14 12:25:11 +00:00
self . gl . tex_parameter_i32 (
2022-01-21 18:51:52 +00:00
glow ::TEXTURE_2D ,
glow ::TEXTURE_MIN_FILTER ,
2022-11-02 16:54:06 +00:00
options . minification . glow_code ( ) as i32 ,
2022-01-21 18:51:52 +00:00
) ;
2022-03-14 12:25:11 +00:00
self . gl . tex_parameter_i32 (
2022-01-21 18:51:52 +00:00
glow ::TEXTURE_2D ,
glow ::TEXTURE_WRAP_S ,
glow ::CLAMP_TO_EDGE as i32 ,
) ;
2022-03-14 12:25:11 +00:00
self . gl . tex_parameter_i32 (
2022-01-21 18:51:52 +00:00
glow ::TEXTURE_2D ,
glow ::TEXTURE_WRAP_T ,
glow ::CLAMP_TO_EDGE as i32 ,
) ;
2022-03-22 15:04:06 +00:00
check_for_gl_error! ( & self . gl , " tex_parameter " ) ;
2022-01-21 18:51:52 +00:00
2022-01-22 10:23:12 +00:00
let ( internal_format , src_format ) = if self . is_webgl_1 {
2022-09-21 19:30:02 +00:00
let format = if self . srgb_textures {
2022-01-21 18:51:52 +00:00
glow ::SRGB_ALPHA
} else {
glow ::RGBA
} ;
2022-01-22 10:23:12 +00:00
( format , format )
2022-09-21 19:30:02 +00:00
} else if self . srgb_textures {
2022-01-22 10:23:12 +00:00
( glow ::SRGB8_ALPHA8 , glow ::RGBA )
2022-09-21 19:30:02 +00:00
} else {
( glow ::RGBA8 , glow ::RGBA )
2022-01-22 10:23:12 +00:00
} ;
2022-03-14 12:25:11 +00:00
self . gl . pixel_store_i32 ( glow ::UNPACK_ALIGNMENT , 1 ) ;
2022-01-22 10:23:12 +00:00
let level = 0 ;
if let Some ( [ x , y ] ) = pos {
2022-03-14 12:25:11 +00:00
self . gl . tex_sub_image_2d (
2022-01-21 18:51:52 +00:00
glow ::TEXTURE_2D ,
2022-01-22 10:23:12 +00:00
level ,
x as _ ,
y as _ ,
w as _ ,
h as _ ,
src_format ,
2022-01-21 18:51:52 +00:00
glow ::UNSIGNED_BYTE ,
2022-01-22 10:23:12 +00:00
glow ::PixelUnpackData ::Slice ( data ) ,
2022-01-21 18:51:52 +00:00
) ;
2022-03-22 15:04:06 +00:00
check_for_gl_error! ( & self . gl , " tex_sub_image_2d " ) ;
2022-01-21 18:51:52 +00:00
} else {
2022-01-22 10:23:12 +00:00
let border = 0 ;
2022-03-14 12:25:11 +00:00
self . gl . tex_image_2d (
2022-01-21 18:51:52 +00:00
glow ::TEXTURE_2D ,
2022-01-22 10:23:12 +00:00
level ,
internal_format as _ ,
w as _ ,
h as _ ,
border ,
src_format ,
2022-01-21 18:51:52 +00:00
glow ::UNSIGNED_BYTE ,
Some ( data ) ,
) ;
2022-03-22 15:04:06 +00:00
check_for_gl_error! ( & self . gl , " tex_image_2d " ) ;
2022-01-21 18:51:52 +00:00
}
2021-10-18 21:13:32 +00:00
}
}
2022-03-14 12:25:11 +00:00
pub fn free_texture ( & mut self , tex_id : egui ::TextureId ) {
2022-01-15 12:59:52 +00:00
if let Some ( old_tex ) = self . textures . remove ( & tex_id ) {
2022-03-14 12:25:11 +00:00
unsafe { self . gl . delete_texture ( old_tex ) } ;
2022-01-15 12:59:52 +00:00
}
2021-10-18 21:13:32 +00:00
}
2022-03-14 12:25:11 +00:00
/// Get the [`glow::Texture`] bound to a [`egui::TextureId`].
2022-08-08 10:21:53 +00:00
pub fn texture ( & self , texture_id : egui ::TextureId ) -> Option < glow ::Texture > {
2022-01-15 12:59:52 +00:00
self . textures . get ( & texture_id ) . copied ( )
2021-10-18 21:13:32 +00:00
}
2022-08-08 10:21:53 +00:00
#[ deprecated = " renamed 'texture' " ]
pub fn get_texture ( & self , texture_id : egui ::TextureId ) -> Option < glow ::Texture > {
self . texture ( texture_id )
}
2022-04-29 06:17:49 +00:00
#[ allow(clippy::needless_pass_by_value) ] // False positive
pub fn register_native_texture ( & mut self , native : glow ::Texture ) -> egui ::TextureId {
self . assert_not_destroyed ( ) ;
let id = egui ::TextureId ::User ( self . next_native_tex_id ) ;
self . next_native_tex_id + = 1 ;
self . textures . insert ( id , native ) ;
id
}
#[ allow(clippy::needless_pass_by_value) ] // False positive
pub fn replace_native_texture ( & mut self , id : egui ::TextureId , replacing : glow ::Texture ) {
if let Some ( old_tex ) = self . textures . insert ( id , replacing ) {
self . textures_to_destroy . push ( old_tex ) ;
}
}
2022-12-04 16:27:40 +00:00
pub fn read_screen_rgba ( & self , [ w , h ] : [ u32 ; 2 ] ) -> Vec < u8 > {
let mut pixels = vec! [ 0_ u8 ; ( w * h * 4 ) as usize ] ;
unsafe {
self . gl . read_pixels (
0 ,
0 ,
w as _ ,
h as _ ,
glow ::RGBA ,
glow ::UNSIGNED_BYTE ,
glow ::PixelPackData ::Slice ( & mut pixels ) ,
) ;
}
pixels
}
pub fn read_screen_rgb ( & self , [ w , h ] : [ u32 ; 2 ] ) -> Vec < u8 > {
let mut pixels = vec! [ 0_ u8 ; ( w * h * 3 ) as usize ] ;
unsafe {
self . gl . read_pixels (
0 ,
0 ,
w as _ ,
h as _ ,
glow ::RGB ,
glow ::UNSIGNED_BYTE ,
glow ::PixelPackData ::Slice ( & mut pixels ) ,
) ;
}
pixels
}
2022-03-14 12:25:11 +00:00
unsafe fn destroy_gl ( & self ) {
self . gl . delete_program ( self . program ) ;
2022-01-15 12:59:52 +00:00
for tex in self . textures . values ( ) {
2022-03-14 12:25:11 +00:00
self . gl . delete_texture ( * tex ) ;
2021-10-18 21:13:32 +00:00
}
2022-03-22 22:11:27 +00:00
self . gl . delete_buffer ( self . vbo ) ;
2022-03-14 12:25:11 +00:00
self . gl . delete_buffer ( self . element_array_buffer ) ;
2021-12-26 20:21:28 +00:00
for t in & self . textures_to_destroy {
2022-03-14 12:25:11 +00:00
self . gl . delete_texture ( * t ) ;
2021-10-18 21:13:32 +00:00
}
}
2022-03-14 12:25:11 +00:00
/// This function must be called before [`Painter`] is dropped, as [`Painter`] has some OpenGL objects
2021-10-18 21:13:32 +00:00
/// that should be deleted.
2022-03-14 12:25:11 +00:00
pub fn destroy ( & mut self ) {
2021-12-28 13:24:59 +00:00
if ! self . destroyed {
unsafe {
2022-03-14 12:25:11 +00:00
self . destroy_gl ( ) ;
2021-11-03 18:17:07 +00:00
}
2021-12-28 13:24:59 +00:00
self . destroyed = true ;
2021-10-18 21:13:32 +00:00
}
}
fn assert_not_destroyed ( & self ) {
2021-12-28 13:24:59 +00:00
assert! ( ! self . destroyed , " the egui glow has already been destroyed! " ) ;
2021-10-18 21:13:32 +00:00
}
}
2021-11-03 18:35:20 +00:00
2023-02-04 10:02:15 +00:00
pub fn clear ( gl : & glow ::Context , screen_size_in_pixels : [ u32 ; 2 ] , clear_color : [ f32 ; 4 ] ) {
2022-05-12 07:02:28 +00:00
crate ::profile_function! ( ) ;
2021-11-03 18:17:07 +00:00
unsafe {
gl . disable ( glow ::SCISSOR_TEST ) ;
2022-03-27 13:20:45 +00:00
gl . viewport (
0 ,
0 ,
screen_size_in_pixels [ 0 ] as i32 ,
screen_size_in_pixels [ 1 ] as i32 ,
) ;
2023-02-04 10:02:15 +00:00
gl . clear_color (
clear_color [ 0 ] ,
clear_color [ 1 ] ,
clear_color [ 2 ] ,
clear_color [ 3 ] ,
) ;
2021-11-03 18:17:07 +00:00
gl . clear ( glow ::COLOR_BUFFER_BIT ) ;
}
}
2021-11-03 18:35:20 +00:00
2021-10-18 21:13:32 +00:00
impl Drop for Painter {
fn drop ( & mut self ) {
2021-12-28 13:24:59 +00:00
if ! self . destroyed {
2022-02-01 11:27:39 +00:00
tracing ::warn! (
2021-12-28 13:24:59 +00:00
" You forgot to call destroy() on the egui glow painter. Resources will leak! "
) ;
}
2021-10-18 21:13:32 +00:00
}
}
2021-11-03 18:17:07 +00:00
2022-03-14 12:25:11 +00:00
fn set_clip_rect (
gl : & glow ::Context ,
size_in_pixels : ( u32 , u32 ) ,
pixels_per_point : f32 ,
clip_rect : Rect ,
) {
// Transform clip rect to physical pixels:
let clip_min_x = pixels_per_point * clip_rect . min . x ;
let clip_min_y = pixels_per_point * clip_rect . min . y ;
let clip_max_x = pixels_per_point * clip_rect . max . x ;
let clip_max_y = pixels_per_point * clip_rect . max . y ;
2022-07-28 22:06:08 +00:00
// Round to integer:
2022-03-14 12:25:11 +00:00
let clip_min_x = clip_min_x . round ( ) as i32 ;
let clip_min_y = clip_min_y . round ( ) as i32 ;
let clip_max_x = clip_max_x . round ( ) as i32 ;
let clip_max_y = clip_max_y . round ( ) as i32 ;
2022-07-28 22:06:08 +00:00
// Clamp:
let clip_min_x = clip_min_x . clamp ( 0 , size_in_pixels . 0 as i32 ) ;
let clip_min_y = clip_min_y . clamp ( 0 , size_in_pixels . 1 as i32 ) ;
let clip_max_x = clip_max_x . clamp ( clip_min_x , size_in_pixels . 0 as i32 ) ;
let clip_max_y = clip_max_y . clamp ( clip_min_y , size_in_pixels . 1 as i32 ) ;
2022-03-14 12:25:11 +00:00
unsafe {
gl . scissor (
clip_min_x ,
size_in_pixels . 1 as i32 - clip_max_y ,
clip_max_x - clip_min_x ,
clip_max_y - clip_min_y ,
) ;
}
}