egui
egui copied to clipboard
Using VertexAttribPointer makes the Ui disappear after a few frames
Describe the bug I'm trying to use eframe with the egui_glow backend to create an application that uses glow to render part of the UI using custom GL code. This works find with the example provided as custom_3d_glow. However, when I slightly modify this example to use vertex attributes, the UI vanishes (usually after 100ms).
I only tried this on Windows desktop so far, using the current Git master version of both egui and eframe. The issue persists even when asking the application to rerender after every frame.
Am I overlooking something with glow or am I doing something else wrong? Thanks in advance.
Here's the modified example code:
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
#![allow(unsafe_code)]
use eframe::{egui, glow::{self, NONE}};
use egui::mutex::Mutex;
use std::sync::Arc;
fn main() {
let options = eframe::NativeOptions {
initial_window_size: Some(egui::vec2(350.0, 380.0)),
multisampling: 8,
renderer: eframe::Renderer::Glow,
..Default::default()
};
eframe::run_native(
"Custom 3D painting in eframe using glow",
options,
Box::new(|cc| Box::new(MyApp::new(cc))),
);
}
struct MyApp {
/// Behind an `Arc<Mutex<…>>` so we can pass it to [`egui::PaintCallback`] and paint later.
rotating_triangle: Arc<Mutex<RotatingTriangle>>,
angle: f32,
}
impl MyApp {
fn new(cc: &eframe::CreationContext<'_>) -> Self {
let gl = cc
.gl
.as_ref()
.expect("You need to run eframe with the glow backend");
Self {
rotating_triangle: Arc::new(Mutex::new(RotatingTriangle::new(gl))),
angle: 0.0,
}
}
}
impl eframe::App for MyApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
ui.horizontal(|ui| {
ui.spacing_mut().item_spacing.x = 0.0;
ui.label("The triangle is being painted using ");
ui.hyperlink_to("glow", "https://github.com/grovesNL/glow");
ui.label(" (OpenGL).");
});
egui::Frame::canvas(ui.style()).show(ui, |ui| {
self.custom_painting(ui);
});
ui.label("Drag to rotate!");
});
ctx.request_repaint();
}
fn on_exit(&mut self, gl: Option<&glow::Context>) {
if let Some(gl) = gl {
self.rotating_triangle.lock().destroy(gl);
}
}
}
impl MyApp {
fn custom_painting(&mut self, ui: &mut egui::Ui) {
let (rect, response) =
ui.allocate_exact_size(egui::Vec2::splat(300.0), egui::Sense::drag());
self.angle += response.drag_delta().x * 0.01;
// Clone locals so we can move them into the paint callback:
let angle = self.angle;
let rotating_triangle = self.rotating_triangle.clone();
let callback = egui::PaintCallback {
rect,
callback: std::sync::Arc::new(egui_glow::CallbackFn::new(move |_info, painter| {
rotating_triangle.lock().paint(painter.gl(), angle);
})),
};
ui.painter().add(callback);
}
}
struct RotatingTriangle {
program: glow::Program,
buffer: glow::Buffer
}
impl RotatingTriangle {
fn new(gl: &glow::Context) -> Self {
use glow::HasContext as _;
let shader_version = if cfg!(target_arch = "wasm32") {
"#version 300 es"
} else {
"#version 330"
};
unsafe {
let program = gl.create_program().expect("Cannot create program");
let (vertex_shader_source, fragment_shader_source) = (
r#"
/*const vec2 verts[3] = vec2[3](
vec2(0.0, 1.0),
vec2(-1.0, -1.0),
vec2(1.0, -1.0)
);
const vec4 colors[3] = vec4[3](
vec4(1.0, 0.0, 0.0, 1.0),
vec4(0.0, 1.0, 0.0, 1.0),
vec4(0.0, 0.0, 1.0, 1.0)
);*/
in vec2 vertex;
in vec4 color;
out vec4 v_color;
uniform float u_angle;
void main() {
v_color = color;
gl_Position = vec4(vertex, 0.0, 1.0);
gl_Position.x *= cos(u_angle);
}
"#,
r#"
precision mediump float;
in vec4 v_color;
out vec4 out_color;
void main() {
out_color = v_color;
}
"#,
);
let shader_sources = [
(glow::VERTEX_SHADER, vertex_shader_source),
(glow::FRAGMENT_SHADER, fragment_shader_source),
];
let shaders: Vec<_> = shader_sources
.iter()
.map(|(shader_type, shader_source)| {
let shader = gl
.create_shader(*shader_type)
.expect("Cannot create shader");
gl.shader_source(shader, &format!("{}\n{}", shader_version, shader_source));
gl.compile_shader(shader);
if !gl.get_shader_compile_status(shader) {
panic!("{}", gl.get_shader_info_log(shader));
}
gl.attach_shader(program, shader);
shader
})
.collect();
gl.link_program(program);
if !gl.get_program_link_status(program) {
panic!("{}", gl.get_program_info_log(program));
}
for shader in shaders {
gl.detach_shader(program, shader);
gl.delete_shader(shader);
}
let positions: Vec<f32> = vec![
0.0, 1.0, 1.0, 0.0, 0.0, 1.0,
-1.0, -1.0, 0.0, 1.0, 0.0, 1.0,
1.0, -1.0, 0.0, 0.0, 1.0, 1.0
];
let u8_slice: &[u8] = std::slice::from_raw_parts(positions.as_ptr() as *const u8, positions.len() * std::mem::size_of::<f32>());
let buffer = gl.create_buffer().unwrap();
gl.bind_buffer(glow::ARRAY_BUFFER, Some(buffer));
gl.buffer_data_u8_slice(glow::ARRAY_BUFFER, u8_slice, glow::STATIC_DRAW);
gl.bind_buffer(glow::ARRAY_BUFFER, None);
Self {
program,
buffer,
}
}
}
fn destroy(&self, gl: &glow::Context) {
use glow::HasContext as _;
unsafe {
gl.delete_program(self.program);
gl.delete_buffer(self.buffer);
}
}
fn paint(&self, gl: &glow::Context, angle: f32) {
use glow::HasContext as _;
unsafe {
gl.use_program(Some(self.program));
gl.uniform_1_f32(
gl.get_uniform_location(self.program, "u_angle").as_ref(),
angle,
);
let vertex_location = gl.get_attrib_location(self.program, "vertex").unwrap();
let color_location = gl.get_attrib_location(self.program, "color").unwrap();
gl.enable_vertex_attrib_array(vertex_location);
gl.enable_vertex_attrib_array(color_location);
gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.buffer));
gl.vertex_attrib_pointer_f32(vertex_location, 2, glow::FLOAT, false, 6 * 4, 0);
gl.vertex_attrib_pointer_f32(color_location, 4, glow::FLOAT, false, 6 * 4, 2 * 4);
gl.draw_arrays(glow::TRIANGLES, 0, 3);
gl.disable_vertex_attrib_array(vertex_location);
gl.disable_vertex_attrib_array(color_location);
gl.bind_buffer(glow::ARRAY_BUFFER, None);
}
}
}
Maybe driver related problem? No problem on OS : Windows 11 21H2 GPU : AMD Radeon vega (Ryzen 5 5500U integrated GPU) Driver version: 22.08.1462311
also working on OS : Ubuntu 22.04 LTS GPU mesa D3D12
mesa D3D12 is working on Windows and Linux . You can download pre built binary from https://github.com/pal1000/mesa-dist-win/releases .
I'm not sure it's a driver related problem.
On Ubuntu 20.04 LTS (Intel HD 4400 and Mesa 21.2.6) I get the following image:
I also tried a Windows 10 Laptop with an embedded Radeon Vega 10 GPU (Driver Version 27.10.11012.10001) and got the same result as on yet another windows computer, with a Radeon RX6900 (Driver Version 30.0.15021.11005). My original example was run on a windows computer with an Nvidia GTX1060 (Driver Version 30.0.14.7111).
Did you modify the code I posted above in some way?
I think I found the problem.
If I modify the paint
-function to bind None
to the vertex array at the very beginning of the unsafe block like this:
fn paint(&self, gl: &glow::Context, angle: f32) {
use glow::HasContext as _;
unsafe {
gl.bind_vertex_array(None);
gl.use_program(Some(self.program));
it works.
In theory this should be accomplished by the unbind
-Function here: https://github.com/emilk/egui/blob/master/crates/egui_glow/src/vao.rs#L97, but when the callback function is called (here: https://github.com/emilk/egui/blob/d5933daee5328d3ca37741c0642d09a1e71232fb/crates/egui_glow/src/painter.rs#L427), things like the post process are still bound.
It seems like that could be the culprit?
No modify. I pasted and compiled as you pasted.