Using VertexAttribPointer makes the Ui disappear after a few frames
- Dominant language
- Rust
- Stars
- 30.6k
- Forks
- 2.1k
- Avg merge
- 1d 12h
- Merged PRs (30d)
- 67
Description
**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>` so we can pass it to [`egui::PaintCallback`] and paint later.
rotating_triangle: Arc>,
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 = 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::());
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);
}
}
}
```
Contributor guide
Assessment
This issue has not been assessed yet.