Skip to content
This repository has been archived by the owner on Jun 8, 2021. It is now read-only.

Add simple opengl example #44

Closed
wants to merge 14 commits into from
Closed

Conversation

mjkoo
Copy link

@mjkoo mjkoo commented Nov 2, 2015

Added example of using a GtkGLArea from gtk-rs, currently the method of loading the required OpenGL functions is somewhat hacky (create an invisible window with glutin and use it's get_proc_address function to do the required name resolution). C examples tend to use a library called epoxy which does platform-independent GL function loading itself, having a similar functionality from within glutin would probably be better so you can still use the platform detection and library loading stuff without needing to actually construct a context/window. Unsure if this is possible with glutin as it stands currently, but after briefly perusing the code it does not seem to be.

@GuillaumeGomez
Copy link
Member

Thanks ! Can you squash your first commit please ?

cc @gkoz

@mjkoo
Copy link
Author

mjkoo commented Nov 2, 2015

Sure, think I did that right?

@GuillaumeGomez
Copy link
Member

Yup, that's good ! Thanks. :)

Cargo.toml Outdated
@@ -5,6 +5,8 @@ authors = ["The Gtk-rs Project Developers"]

[dependencies]
libc = "0.1"
gl = { version = "^0", optional = true }
glutin = { version = "^0", optional = true }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be a good idea to clamp down on the allowed versions a bit? What if glutin 0.5 removes WindowBuilder?

@gkoz
Copy link
Member

gkoz commented Nov 2, 2015

Please add this patch to make travis build the non-dummy version of the example:

--- a/build.sh
+++ b/build.sh
@@ -5,7 +5,7 @@ set -e

 if [ "$GTK" = latest ]; then
        BUNDLE="gtk-3.18.1-2"
-       FEATURES=gtk_3_10
+       FEATURES="gtk_3_16 opengl"
 fi

 if [ -n "$BUNDLE" ]; then

…on required versions for OpenGL dependencies, add comment explaining glutin use in glarea example
@mjkoo
Copy link
Author

mjkoo commented Nov 2, 2015

Sure, hopefully this is better. I believe that the travis build failed as the example requires the modified GLArea stuff from the PR I submitted to the gtk repo (gtk-rs/gtk#185)

@gkoz
Copy link
Member

gkoz commented Nov 2, 2015

The build succeeds now.

@tomaka has hinted on #rust-fr that this hack is likely to break. I'm hoping we'll get something more definite but it seems there might need to be a "THIS IS UNSAFE AND BROKEN" warning.

@GuillaumeGomez
Copy link
Member

So here's the explanation from @tomaka:

it crashes because loaded functions depend of the context
the glClear function from context 1 might be not the same glClear function in context 2
if you're lucky and it's the same one, it will work
otherwise, it crashes

however, it's not "likely to break"
there is 98% chances that it'll work
but it's not clean

He also said that there was no "official" way to do it currently. :)

@mjkoo
Copy link
Author

mjkoo commented Nov 3, 2015

Yeah I agree it's a hack, not sure about the chances of breakage though. Looked through the libepoxy source, their approach basically comes down to picking between glXGetProcAddressARB / wglGetProcAddress / eglGetProcAddress / dlsym based on what platform it was built for (https://github.com/anholt/libepoxy/blob/master/src/dispatch_common.c#L651). libepoxy seems to be the "official" way to do this according to the gtkglarea examples from Gnome (https://github.com/GNOME/gtkglarea/blob/master/examples/gtkglarea_demo.c) Re-using glutin in this way is hacky, but comes out to pretty much doing the same thing, although it may be more comfortable if we just port this bit of logic from epoxy to rust to avoid abusing the glutin API in this way?

Either way to me it looks like GtkGLArea creates a context for you, but relies on you to correctly infer the correct way to load and use OpenGL functions after initialization, so the same type of logic with the same type of assumptions probably needs to go somewhere to make this usable.

Would you all rather I put in a large "HACKS AND BROKENNESS" warning, look at implementing libepoxy-style choosing between variants of *GetProcAddress, or hold off until there's a better way to achieve this in gtk or glutin/glium?

Somewhat of a side-note, what do you all / @tomaka think should be the way forward with regards to glium integration? I started looking at what would need to happen for that to work, looks like one would need to create a glium::backend::{Backend, Facade} implementation which wraps a GLArea at a minimum, but it's early days of me trying to grok how these parts of glium work under the hood.

@gkoz
Copy link
Member

gkoz commented Nov 3, 2015

@mjkoo We'd be very happy if you investigated proper (or at least more reliable) ways of integrating GdkGLContext and glium. I feel, the example should wait until we find a better way or perhaps a conclusive lack of one. Let's take this to https://github.com/gtk-rs/gdk/issues/81

@GuillaumeGomez
Copy link
Member

If we don't find a "perfect" / "official" solution, I'm okay with merging this PR if we add a complete explanation of what's going on in order to avoid losing people if some are interested in using opengl in GTK.

@mjkoo
Copy link
Author

mjkoo commented Nov 3, 2015

@GuillaumeGomez I think there is a better solution, as it stands this version isn't really suitable as it leads to conflicts between gl-rs's name resolution and epoxy's, more details in the discussion over at https://github.com/gtk-rs/gdk/issues/81

src/glarea.rs Outdated

let window = Window::new(gtk::WindowType::Toplevel).unwrap();

let cell = Rc::new(RefCell::new(GLArea::new().unwrap()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any GObject already is a refcounted shared reference so you don't need to add another layer of that. Cloning will just add to refcount and all methods take &self.

@gkoz
Copy link
Member

gkoz commented Nov 4, 2015

The realize signal receives a reference to glarea as an argument of type Widget, but it doesn't seem like there's a way to downcast this to a GLArea to invoke make_current, short of maybe extracting the underlying pointer and constructing a dummy GLArea wrapper around it (which seemed to be all sorts of wrong) hence this solution using Rc. Is there a better way to do this?

There will be a way to downcast objects once the object reform lands. I'll also look into making connect_* functions generic to hide that downcast under the hood.

@mjkoo
Copy link
Author

mjkoo commented Nov 4, 2015

Cool thanks, that makes sense now, Rc stuff removed.

@gkoz
Copy link
Member

gkoz commented Nov 4, 2015

The amount of unsafe code makes me nervous. How much of it is due to the lack of glium integration?

@mjkoo
Copy link
Author

mjkoo commented Nov 4, 2015

Pretty much all of it, the epoxy stuff works essentially identically to the gl crate which also has a good amount of unsafe code due to passing pointers in and out of the raw OpenGL functions (see https://github.com/bjz/gl-rs/blob/master/gl/examples/static_triangle.rs for the example this code was based on).

src/glarea.rs Outdated
let shader = Gl.CreateShader(ty);
// Attempt to compile the shader
let csrc = ffi::CString::new(src).unwrap().as_ptr();
Gl.ShaderSource(shader, 1, &csrc, ptr::null());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, it seems you're not required to use CString here because the function takes an array of lengths?
I'm somewhat allergic to CString because it's a popular source of accidental dangling pointers (not that you did anything wrong here).

src/glarea.rs Outdated
if status != (epoxy::TRUE as GLint) {
let mut len = 0;
Gl.GetShaderiv(shader, epoxy::INFO_LOG_LENGTH, &mut len);
let mut buf = vec![0u8; len as usize - 1];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't len "the size of the character buffer required to store the information log)" making len - 1 too short?

@gkoz
Copy link
Member

gkoz commented Nov 4, 2015

Regarding the shader compiling functions, are the errors "static" in the sense that broken code won't compile and that's it, or can they depend on the hardware and the driver? In the latter case we might want to handle the error more gracefully than panicing.

Also it appears GLArea::new something just kills the process with "glarea: Couldn't find current GLX or EGL context" if a compatible GL implementation is missing. Could that be checked beforehand too?

src/glarea.rs Outdated
let mut len: GLint = 0;
Gl.GetProgramiv(program, epoxy::INFO_LOG_LENGTH, &mut len);
let mut buf = Vec::with_capacity(len as usize);
buf.set_len((len as usize) - 1); // Skip trailing null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd still be cautious when writing past the "official" length. And I just don't see why buf = vec![0; len as usize] would be inadequate. You can ignore the null byte later by taking a slice or letting CStr take care of both that and utf8 compliance:

CStr::from_ptr(buf.as_ptr()).to_string_lossy()

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay that sounds good, will make that change. In general writing to a Vec within its allocated capacity should be "okay" though (for some definition of okay)?

@mjkoo
Copy link
Author

mjkoo commented Nov 4, 2015

Fixed the issue with the shader compilation error fetching and printing, I think I originally was having issues compiling that that caused me to change it, not 100% sure why but the previous version definitely was off by one (good catch!) and just ate the actual shader compilation error. Brought it back to be in line with the implementation from the gl example.

Shader compilation will depend on the hw and driver, not sure what would be a better way of handling it for this example application though?

That error message appears to be from libepoxy itself (https://github.com/anholt/libepoxy/blob/master/src/dispatch_common.c#L674), perhaps we could do a check against epoxy_gl_version before attempting to construct the GLArea? Not sure what that would report in the error case.

@gkoz
Copy link
Member

gkoz commented Nov 4, 2015

Shader compilation will depend on the hw and driver, not sure what would be a better way of handling it for this example application though?

An ideal way could be along the lines of using idle_add to schedule a closure, that would show a message dialog with the error and then exit the app.

Panicing in a signal handler (which is a C callback) should be avoided at all cost because currently we just ignore the issue of unwinding across the FFI boundary ;)

@gkoz
Copy link
Member

gkoz commented Nov 4, 2015

An ideal way could be along the lines of using idle_add to schedule a closure, that would show a message dialog with the error and then exit the app.

This might be too complex for its own good. If we defer the error message then the render signal needs to do some checking too. Perhaps showing the error dialog directly from the realize handler is okay.

@GuillaumeGomez
Copy link
Member

@mjkoo: Hi! The gtk crate has well evolved. Are you interested in updating this PR to the last version so we can merge it? Having an opengl example in gtk-rs would be really awesome!

@mjkoo
Copy link
Author

mjkoo commented Mar 4, 2016

Sure, I'll take a look at it this weekend.

@GuillaumeGomez
Copy link
Member

Great! Thanks a lot!

@GuillaumeGomez
Copy link
Member

Closing due to inactivity. Don't hesitate to reopen once updated. :)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants