You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have been creating a few OpenGL widgets in the same way as the gtk4-rs example glium_gl_area. They have worked well up until when I wanted to draw a texture. I can't for the life of me get it to render the texture. To load and render textures I started from the image example in the glium crate.
I have created a "minimal" example of my issue where I have factored out the texture loading and GL drawing code to a module and then use the exact same code to draw a texture in a winit window and a gtk4 window. It works as expected in the winit window but nothing is drawn in the gtk4 window. My code is available here: https://github.com/faern/gtk_glium_texture/. I'm also copying all the code here, so it can be read without leaving the issue:
main.rs
use std::ptr;use gtk::{glib, prelude::*};mod glium_gl_area;use glium_gl_area::GliumGLArea;pubmod gl;mod glium;fnmain() -> glib::ExitCode{// Load GL pointers from epoxy (GL context management library used by GTK).{#[cfg(target_os = "macos")]let library = unsafe{ libloading::os::unix::Library::new("libepoxy.0.dylib")}.unwrap();#[cfg(all(unix, not(target_os = "macos")))]let library = unsafe{ libloading::os::unix::Library::new("libepoxy.so.0")}.unwrap();#[cfg(windows)]let library = libloading::os::windows::Library::open_already_loaded("libepoxy-0.dll").or_else(|_| libloading::os::windows::Library::open_already_loaded("epoxy-0.dll")).unwrap();
epoxy::load_with(|name| {unsafe{ library.get::<_>(name.as_bytes())}.map(|symbol| *symbol).unwrap_or(ptr::null())});}// Spawn glium window (that works and draws texture!) in a separate thread
std::thread::spawn(move || {
glium::State::run_loop();});// Run GTK window (where textures don't render!) in main threadlet application = gtk::Application::builder().application_id("com.example.not-working-gtk-textures").build();
application.connect_activate(build_ui);
application.run()}fnbuild_ui(application:>k::Application){let window = gtk::ApplicationWindow::new(application);
window.set_title(Some("Glium in GLArea"));let widget = GliumGLArea::default();
window.set_child(Some(&widget));
window.present();}
mod imp;use gtk::{gdk, glib, prelude::*};
glib::wrapper! {pubstructGliumGLArea(ObjectSubclass<imp::GliumGLArea>)
@extends gtk::GLArea, gtk::Widget;}implDefaultforGliumGLArea{fndefault() -> Self{
glib::Object::new()}}unsafeimpl glium::backend::BackendforGliumGLArea{fnswap_buffers(&self) -> Result<(), glium::SwapBuffersError>{// We're supposed to draw (and hence swap buffers) only inside the `render()`// vfunc or signal, which means that GLArea will handle buffer swaps for// us.Ok(())}unsafefnget_proc_address(&self,symbol:&str) -> *const std::ffi::c_void{
epoxy::get_proc_addr(symbol)}fnget_framebuffer_dimensions(&self) -> (u32,u32){let scale = self.scale_factor();let width = self.width();let height = self.height();((width * scale)asu32,(height * scale)asu32)}fnis_current(&self) -> bool{matchself.context(){Some(context) => gdk::GLContext::current() == Some(context),None => false,}}unsafefnmake_current(&self){GLAreaExt::make_current(self);}fnresize(&self,size:(u32,u32)){self.set_size_request(size.0asi32, size.1asi32);}}
glium_gl_area/imp.rs
use std::{cell::RefCell, rc::Rc};use glium::Frame;use gtk::{glib, prelude::*, subclass::prelude::*};structRenderer{context:Rc<glium::backend::Context>,draw_texture:crate::gl::DrawTexture,}implRenderer{fnnew(context:Rc<glium::backend::Context>) -> Self{let draw_texture = crate::gl::DrawTexture::new(&context);Self{
context,
draw_texture,}}fndraw(&self){let frame = Frame::new(self.context.clone(),self.context.get_framebuffer_dimensions(),);self.draw_texture.draw(frame);}}#[derive(Default)]pubstructGliumGLArea{renderer:RefCell<Option<Renderer>>,}#[glib::object_subclass]implObjectSubclassforGliumGLArea{constNAME:&'static str = "GliumGLArea";typeType = super::GliumGLArea;typeParentType = gtk::GLArea;}implObjectImplforGliumGLArea{}implWidgetImplforGliumGLArea{fnrealize(&self){self.parent_realize();let widget = self.obj();if widget.error().is_some(){return;}// SAFETY: we know the GdkGLContext exists as we checked for errors above, and// we haven't done any operations on it which could lead to glium's// state mismatch. (In theory, GTK doesn't do any state-breaking// operations on the context either.)//// We will also ensure glium's context does not outlive the GdkGLContext by// destroying it in `unrealize()`.let context = unsafe{
glium::backend::Context::new(
widget.clone(),true,
glium::debug::DebugCallbackBehavior::PrintAll,)}.unwrap();*self.renderer.borrow_mut() = Some(Renderer::new(context));}fnunrealize(&self){*self.renderer.borrow_mut() = None;self.parent_unrealize();}}implGLAreaImplforGliumGLArea{fnrender(&self,_context:>k::gdk::GLContext) -> glib::Propagation{self.renderer.borrow().as_ref().unwrap().draw();
glib::Propagation::Stop}}
glium.rs
use glium::Display;use glutin::display::GetGlDisplay;use glutin::prelude::*;use glutin::surface::WindowSurface;use raw_window_handle::HasRawWindowHandle;use std::num::NonZeroU32;use winit::platform::wayland::EventLoopBuilderExtWayland;pubstructApplication{draw_texture:crate::gl::DrawTexture,}implApplication{fnnew(display:&Display<WindowSurface>) -> Self{Self{draw_texture:crate::gl::DrawTexture::new(display),}}fndraw_frame(&mutself,display:&Display<WindowSurface>){let frame = display.draw();self.draw_texture.draw(frame);}}pubstructState{pubdisplay: glium::Display<WindowSurface>,pubwindow: winit::window::Window,pubcontext:Application,}implState{pubfnnew<W>(event_loop:&winit::event_loop::EventLoopWindowTarget<W>,visible:bool) -> Self{let window_builder = winit::window::WindowBuilder::new().with_title("Glium image example").with_visible(visible);let config_template_builder = glutin::config::ConfigTemplateBuilder::new();let display_builder =
glutin_winit::DisplayBuilder::new().with_window_builder(Some(window_builder));// First we create a windowlet(window, gl_config) = display_builder
.build(event_loop, config_template_builder, |mut configs| {// Just use the first configuration since we don't have any special preferences here
configs.next().unwrap()}).unwrap();let window = window.unwrap();// Then the configuration which decides which OpenGL version we'll end up using, here we just use the default which is currently 3.3 core// When this fails we'll try and create an ES context, this is mainly used on mobile devices or various ARM SBC's// If you depend on features available in modern OpenGL Versions you need to request a specific, modern, version. Otherwise things will very likely fail.let raw_window_handle = window.raw_window_handle();let context_attributes =
glutin::context::ContextAttributesBuilder::new().build(Some(raw_window_handle));let fallback_context_attributes = glutin::context::ContextAttributesBuilder::new().with_context_api(glutin::context::ContextApi::Gles(None)).build(Some(raw_window_handle));let not_current_gl_context = Some(unsafe{
gl_config
.display().create_context(&gl_config,&context_attributes).unwrap_or_else(|_| {
gl_config
.display().create_context(&gl_config,&fallback_context_attributes).expect("failed to create context")})});// Determine our framebuffer size based on the window size, or default to 800x600 if it's invisiblelet(width, height):(u32,u32) = if visible {
window.inner_size().into()}else{(800,600)};let attrs = glutin::surface::SurfaceAttributesBuilder::<WindowSurface>::new().build(
raw_window_handle,NonZeroU32::new(width).unwrap(),NonZeroU32::new(height).unwrap(),);// Now we can create our surface, use it to make our context current and finally create our displaylet surface = unsafe{
gl_config
.display().create_window_surface(&gl_config,&attrs).unwrap()};let current_context = not_current_gl_context
.unwrap().make_current(&surface).unwrap();let display = glium::Display::from_context_surface(current_context, surface).unwrap();Self::from_display_window(display, window)}pubfnfrom_display_window(display: glium::Display<WindowSurface>,window: winit::window::Window,) -> Self{let context = Application::new(&display);Self{
display,
window,
context,}}/// Start the event_loop and keep rendering frames until the program is closedpubfnrun_loop(){let event_loop = winit::event_loop::EventLoopBuilder::new().with_any_thread(true).build().expect("event loop building");letmut state:Option<State> = None;let result = event_loop.run(move |event, window_target| {match event {// The Resumed/Suspended events are mostly for Android compatiblity since the context can get lost there at any point.// For convenience's sake the Resumed event is also delivered on other platforms on program startup.
winit::event::Event::Resumed => {
state = Some(State::new(window_target,true));}
winit::event::Event::Suspended => state = None,// By requesting a redraw in response to a AboutToWait event we get continuous rendering.// For applications that only change due to user input you could remove this handler.
winit::event::Event::AboutToWait => {ifletSome(state) = &state {
state.window.request_redraw();}}
winit::event::Event::WindowEvent{ event, .. } => match event {
winit::event::WindowEvent::Resized(new_size) => {ifletSome(state) = &state {
state.display.resize(new_size.into());}}
winit::event::WindowEvent::RedrawRequested => {ifletSome(state) = &mut state {
state.context.draw_frame(&state.display);}}// Exit the event loop when requested (by closing the window for example) or when// pressing the Esc key.
winit::event::WindowEvent::CloseRequested
| winit::event::WindowEvent::KeyboardInput{event:
winit::event::KeyEvent{state: winit::event::ElementState::Pressed,logical_key:
winit::keyboard::Key::Named(winit::keyboard::NamedKey::Escape),
..
},
..
} => window_target.exit(),// Every other event
_ev => {}},
_ => (),};});
result.unwrap();}}
Here is a screenshot of the two windows side by side, winit/glium window to the left and gtk to the right:
I run this on Fedora Linux 39 under sway (wayland). But I have tried it under XWayland also and I get the same result. So I don't think it's an X11/Wayland issue.
Since all the texture loading and drawing code is exactly the same I know the texture will have loaded properly, should be bound properly before the draw call(?) etc. The only difference I can think of is if the initialization of the GL contexts are different in a way that cause this? Or a bug in gtk4-rs?
The text was updated successfully, but these errors were encountered:
I have been creating a few OpenGL widgets in the same way as the gtk4-rs example
glium_gl_area
. They have worked well up until when I wanted to draw a texture. I can't for the life of me get it to render the texture. To load and render textures I started from the image example in the glium crate.I have created a "minimal" example of my issue where I have factored out the texture loading and GL drawing code to a module and then use the exact same code to draw a texture in a
winit
window and agtk4
window. It works as expected in thewinit
window but nothing is drawn in thegtk4
window. My code is available here: https://github.com/faern/gtk_glium_texture/. I'm also copying all the code here, so it can be read without leaving the issue:main.rs
gl.rs
glium_gl_area/mod.rs
glium_gl_area/imp.rs
glium.rs
Here is a screenshot of the two windows side by side, winit/glium window to the left and gtk to the right:
I run this on Fedora Linux 39 under sway (wayland). But I have tried it under XWayland also and I get the same result. So I don't think it's an X11/Wayland issue.
Since all the texture loading and drawing code is exactly the same I know the texture will have loaded properly, should be bound properly before the draw call(?) etc. The only difference I can think of is if the initialization of the GL contexts are different in a way that cause this? Or a bug in
gtk4-rs
?The text was updated successfully, but these errors were encountered: