brendanzab / gl-rs

An OpenGL function pointer loader for Rust
Apache License 2.0
680 stars 115 forks source link

Disjoint types are generated for different versions of webgl #476

Open aubreyrjones opened 5 years ago

aubreyrjones commented 5 years ago

I'm new to Rust, so I may be misunderstanding. Please close if so.

It appears that disjoint types are created for different versions of OpenGL. While I understand the appeal, and it's a pattern that shows up in several other frameworks (like Qt and Java), this makes it very hard to write code that targets common subsets of functionality. Every function winds up targeting specific versions in their parameter structure, and upgrading to a new version requires touching all those functions.

In C/C++, one can write OpenGL code using only the API common between ES 3, GL 4.5, and WebGL 2, then swap the actual GL implementation just by choosing a different header file and loader library. Likewise, if I have code that works in desktop 3.3, I can upgrade to 4.4 just by requesting a new context version and getting the new header.

A function in C like void clear_framebuffer() { glClear(GL_COLOR_BUFFER_BIT); } can be recompiled under any version of OpenGL that has ever been written. But I'm struggling to write an equivalent Rust function using these bindings.

I'm too much a beginner at this language to offer a suggested solution. I would guess maybe autogenerated traits? But the current situation appears to limit the portability of code written with at least the WebGL crates.

brendanzab commented 5 years ago

Good thinking! Yeah, I'm not sure what we can do about this, and I think it is worth thinking about! It would certainly be nice to be able to share common interfaces! Maybe traits would indeed be a solution... 🤔

Diggsey commented 5 years ago

The GLContext type is what you're looking for - it can store any version of a webgl context and has methods for all of them.

aubreyrjones commented 5 years ago

@Diggsey oh! I see that type. But I think my Rust ignorance is showing, because I cannot figure out how to assign a "real" context into it. There's some kind of magic going on with the InstanceOf trait, I think... but I don't even know what to google.

Diggsey commented 5 years ago

This is more of a stdweb-ism than a rust-ism, but you can cast between any reference types in stdweb using the TryFrom trait - see here: https://docs.rs/webgl_stdweb/0.3.0/webgl_stdweb/struct.GLContext.html#impl-TryFrom%3CReference%3E

aubreyrjones commented 5 years ago

@Diggsey ah, okay. I went ahead and enabled all the nightly try_from stuff. And I learned in the process that type inference works "both ways" in Rust, and that you can write something like try_into which depends on knowing the type of the receiver! Trippy!

However, now I get the following error which gives me pause:

  --> src/main.rs:30:34
   |
30 |     let ctx: GLContext = context.try_into().unwrap();
   |                                  ^^^^^^^^ the trait `stdweb::unstable::TryFrom<doodle::WebGL2RenderingContext>` is not implemented for `doodle::GLContext`
   |
   = help: the following implementations were found:
             <doodle::GLContext as stdweb::unstable::TryFrom<&'_r stdweb::Reference>>
             <doodle::GLContext as stdweb::unstable::TryFrom<&'_r stdweb::Value>>
             <doodle::GLContext as stdweb::unstable::TryFrom<stdweb::Reference>>
             <doodle::GLContext as stdweb::unstable::TryFrom<stdweb::Value>>
   = note: required because of the requirements on the impl of `stdweb::unstable::TryInto<doodle::GLContext>` for `doodle::WebGL2RenderingContext`

So what I'm seeing is that it's not a Rust conversion, but rather that it's stdweb exploiting the fact that it can send js!{} to either context without any type checking on the back end. I see what you meant.

This is nice, but I actually only support WebGL 2. My target common subset is actually WebGL 2, GL ES 3.0, and OpenGL 4.4 across WASM, ARM, and x86_64 targets respectively. I want the same renderering code to work everywhere with only a recompile. This is a thing in C++, but Rust is a much more attractive language for the browser than C++, so that's what we're going to use.

Am I going to be able to try_into() from a native GL context? Or should I be looking at parsing the IDL and doing codegen for my common subset?