-
Notifications
You must be signed in to change notification settings - Fork 154
Web GL #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
IMO it might be useful to have unified OpenGL, OpenGL ES, and WebGL bindings, similar to the unified bindings Emscripten provides. I already started working on this in https://github.com/grovesNL/glow for use in gfx-hal's GL backend (gfx-backend-gl) and I'll continue to expand it as I work through WebGL support for gfx-hal. With this approach, projects like gfx-hal, glium etc. could use and share these unified bindings instead. |
After reading the Gloo update, I will suggest to start with a minimalistic WebGL and WebGL2 wrapper which have three main goals:
.. So basically the same purpose as Glium but without the simplification of the interface. I think it is important to have the thin Rust-y wrapper on top of WebGL as the innermost layer of the onion and when that is done, there is plenty of room to add outer layers. This could be a simplified and safer interface or the combined OpenGL+WebGL API suggested by @grovesNL. Please don’t hesitate to disagree with some or any of my opinions :-) |
What do y'all thinking of mimicking syntax from GFX/Vulkan when able? I agree that the first step should be a thin wrapper. For later high-level APIs, it would be nice to integrate matrix-manipulation tools. Using cgmath as a dependency is one option, or more generally, ndarray. Or creating our own, with an emphasis on interop with JS TypedArrays. |
I have no opinions here, other than that regl.party has proven to be a useful model to build longer-lasting WebGL applications in JS. Perhaps it's something to draw inspiration from when we eventually build the higher-level layers. |
Here’s a more detailed proposal of the basic WebGL crate after request by @fitzgen. SummarySafe, rusty wrapper for the web-sys WebGL functionality. What is included?
What is not included?
MotivationThe web-sys WebGL functionality is difficult to approach because of the JS types, conversions and memory management. Also, the WebGL API in itself is very far from the Rust way of thinking, for example is it not type safe at all. So the purpose of this crate is to expose a simple and safe API which is similar, though not identical, to the WebGL API. The intended users are graphics programmers at all levels of experience. Detailed explanationHere’s an example implementation: use web_sys;
// There should be a struct for WebGL 2 context too.
pub struct WebGlRenderingContext
{
context: web_sys::WebGlRenderingContext
}
impl WebGlRenderingContext {
// This is the only place where the API is web-sys specific and it should preferably be
// used primarily from the Gloo canvas crate or something, not directly from the user.
pub fn new(web_sys_context: web_sys::WebGlRenderingContext) -> Self
{
WebGlRenderingContext { context: web_sys_context }
}
}
// Then it is 'just' implementing all of the webgl methods from web-sys. Let's start with a
// simple example.
impl WebGlRenderingContext {
// Changing the input types to make sure that the method does not throw a runtime exception
// is an easy win. No other errors can occur in this method so no need to return a result.
pub fn viewport(&self, x: i32, y: i32, width: u32 /*changed from i32*/, height: u32/*changed from i32*/)
{
// Maybe check if the viewport has changed?
self.context.viewport(x, y, width as i32, height as i32);
}
}
// And another example:
#[derive(Debug, Eq, PartialEq)]
pub enum BlendType
{
Zero,
One,
SrcColor,
OneMinusSrcColor,
SrcAlpha,
OneMinusSrcAlpha,
DstAlpha,
OneMinusDstAlpha,
ConstantColor,
ConstantAlpha
// ...
}
impl WebGlRenderingContext {
// Again, the input parameter types makes the interface safe
pub fn blend_func(&self, s_factor: BlendType, d_factor: BlendType) -> Result<(), Error>
{
// Check if ConstantColor and ConstantAlpha is used together (see https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/blendFunc)
if s_factor == BlendType::ConstantColor && d_factor == BlendType::ConstantAlpha ||
s_factor == BlendType::ConstantAlpha && d_factor == BlendType::ConstantColor
{
return Err(Error::WebGLError { message: "blend_func cannot be called with both ConstantColor and ConstantAlpha.".to_string() });
}
// Check if the blend state is already the desired blend state, if it is, then we don't call the webgl function!
// ...
Ok(())
}
}
// So next example is a bit more complex and this involves exposing another API than the one in web-sys/WebGL.
// The goal here is not to copy the WebGL API, but rather to expose the exact same functionality as safely as possible.
pub struct VertexShader<'a>
{
shader: web_sys::WebGlShader,
context: &'a web_sys::WebGlRenderingContext
}
// Delete the shader after it has been used and possibly reused.
impl<'a> std::ops::Drop for VertexShader<'a>
{
fn drop(&mut self)
{
self.context.delete_shader(Some(&self.shader));
}
}
impl WebGlRenderingContext {
pub fn create_vertex_shader(&self, source: &str) -> Result<VertexShader, Error>
{
let shader = self.context.create_shader(web_sys::WebGlRenderingContext::VERTEX_SHADER)
.ok_or(Error::WebGLError {message: "Cannot create vertex shader!".to_string()})?;
self.context.shader_source(&shader, source);
self.context.compile_shader(&shader);
if self.context.get_shader_parameter(&shader, web_sys::WebGlRenderingContext::COMPILE_STATUS).as_bool().unwrap_or(false)
{
Ok(VertexShader {shader, context: &self.context})
} else {
Err(Error::WebGLError { message: self.context.get_shader_info_log(&shader).unwrap_or_else(|| "Unknown error creating shader".into()) })
}
}
pub fn create_program(&self, vertex_shader: VertexShader /*, fragment_shader: FragmentShader*/)
{
// ...
}
}
#[derive(Debug)]
pub enum Error {
WebGLError {message: String}
} Drawbacks, rationale and alternativesThis crate solves the easy problems with the web-sys (and WebGL) interface (rusty safe interface). The more hard problems (simple to use, avoid state machine, performance etc.) is difficult to address in general and especially without creating an opinionated library. Therefore, I envision this crate to be the foundation for a multitude of different opinionated libraries as well as to be used by graphics programmers that want low level control. The alternative, as I see it, is to go straight to the higher level crates and then let graphics programmers use the web-sys API. However, I would have appreciated a crate like this a few months ago and I am probably not the only one. Unresolved questions
|
I like it, and agree that the center should be a Here's one approach: pub struct EnableCfg {
blend: bool,
cull_face: bool,
depth_test: bool,
dither: bool,
// etc
}
impl Default for EnableCfg {
fn default() -> Self {
Self {
dither: true,
blend: false,
// etc
}
}
}
pub struct WebGlRenderingContext
{
context: web_sys::WebGlRenderingContext,
enable_cfg: EnableCfg,
} |
It seems fine for gloo to focus on WebGL only (i.e. instead of both OpenGL and WebGL), but it's also a bit restricting for existing crates which would like to target both OpenGL and WebGL. For example, glium would need to have a separate WebGL backend which uses this new API, and gfx-hal would still need something like glow to map between native and web APIs (possibly still have to use web-sys directly). I also think it would be better to leave state caching and validation out of the innermost layer, because this breaks concepts like GL context sharing for example. |
@David-OConnor Thanks! @grovesNL I am a big fan of what you are doing with glow, but for the basic crate I think it should be purely WebGL as there are differences between OpenGL and WebGL. Also gloo is about web utility crates as you also pointed out. That said, I see nothing wrong with creating a higher level crate that targets both WebGL and OpenGL and then it is up to the users decide which to use. |
I wonder if Gloo's role in this should be simple wrappers that improve the web_sys API, and deal with types better. (eg not expose JsValue, and cast things in ways bindgen can convert to JS typed arrays). There are multiple layers from raw [Web]Gl through full engines, and different APIs like WebGl, OpenGl, Vulkan etc that have mostly-overlapping functionality, with some differences. I think we should leave the mid/high-level things to dedicated, cross-API graphics crates like gfx, but provide a crate they can use to facilitate webgl-in-rust. I've built a dual-API WebGl/Vulkano program, where the models, matrices etc were in Rust, and the WebGl part used |
I think that is exactly the purpose of gloo, though I'm probably not the best to answer that question. |
Continuing the discussion in this PR |
A guide to Rust Graphics Libraries in 2019 has been a very good read. In particular something they pointed at was the work currently being done on amethyst/rendy. I don't think this would preclude any work on wrapping WebGL, but it seems like they're moving moving fast, and perhaps figuring out how to support them might be very valuable. |
👍 The crate I mentioned above (glow) is being used to implement gfx-hal WebGL support, and Rendy/Amethyst use gfx-hal. |
@grovesNL So you are definitely a potential user. Would you have used the suggested crate for glow if it was available? Or do you need something different? @yoshuawuyts Really nice read indeed. |
@asny For gfx-hal I was mostly looking for "raw" unified OpenGL/OpenGL ES/WebGL bindings with the same type signatures, which had common extension loading and version handling (optionally some really simple emulation like glMapBufferRange on WebGL). So basically what Emscripten would normally provide, but lighter weight. For our use case, I'm not sure it's useful to have state caching and validation at this level. For example, we already have to track states internally. I think glium is probably the closest GL crate which provides validation, but it adds some overhead that we'd prefer to avoid. |
I think this is really interesting! I think anyone creating something like gfx-hal, Glium, vulkano etc would prefer low level access with no overhead. |
Yeah I think this is true, although Vulkano is probably unlikely to ever have a WebGL backend because it targets Vulkan directly.
This is one of the goals of glow. Currently all JS types are only used internally, and the API only exposes primitives or its own types.
It's definitely an option, but could be a pretty large effort based on the size of Glium. I think it would be interesting to have Glium use unified bindings (like glow), allowing it to add WebGL support without having to do much work (i.e. adding a new backend or flags). This would allow people already using Glium to get WebGL support pretty quickly.
There could be some interesting ideas here – basically have some optional libraries built on top of low level bindings. I think some ideas might include: resource loading (async loading of images and then uploading them into GL textures), extension handling (checking whether functionality is enabled, fallbacks from WebGL2 to WebGL1 extensions), version parsing, state caching (built on top of the lower level bindings but otherwise like you described), ESSL shader reflection, etc. I already plan to add some kind of extension handling and version parsing in glow for example. There might also be some good inspiration in WebGL libraries or engines, like regl, pex, three.js, the new abstraction in pixi.js, etc. |
Yeah, I totally agree. What I meant was that Vulkano wants low level access to Vulkan, Glium to OpenGL etc.
I have to say that I am sceptical about Glium ever getting a WebGL backend, but you’ll never know. Also, I think this is part of a larger discussion whether to have a general API and multiple backends (like gfx and most game engines) or having an API which corresponds to a specific backend (like Glium, Vulkano etc.). One is not better than the other in my opinion, it very much depends on what you want to use it for. If you want to target all platforms and have limited resources, one API for multiple backends are definitely the way forward. But if you only want to target web, using a web specific API would probably save you some trouble down the road. Actually, I read a long discussion between Tomaka and Kvark about this (which I cannot find at the moment) and I don’t want to go down that road. Gloo is about web and it seems like the one-API-multiple-backends side is covered by for example yourself, so let’s focus on WebGL.
Yeah, I like it as well (actually, Three.js also do this, which I really liked)! Instead of incapsulating the web-sys rendering context, you always have access to it and are never limited by the API. This means that we can avoid handling edge cases and thereby expose a simpler interface. Also this will be helpful with only a limited part implemented since you can just use web-sys for the rest. Finally, this means complete independence between for example the program/shader setup and texture loading. The downside is that we cannot keep track of the states, since we never know if the user changed it directly through web-sys, but I think that is a small price to pay. I will do another proof-of-concept version of the API that reflects these thoughts, but it might take a while before I have time. Can you elaborate a bit about the ideas behind version parsing and ESSL shader reflection?
I'll look more into them. |
This comment was marked as abuse.
This comment was marked as abuse.
Apparently, I am the only one answering so let me answer you as well :) Nice project! It is indeed very similar to what this project is about, but I don’t think it has been settled yet how this project will turn out. It is definitely nice to avoid retrieving the uniform location using webgl, but I am unsure how much of a performance impact it will actually do. A way to do lazy construction in rust is to wrap your variable in an Option. In this case it is Option<HashMap<String, u32>> and then construct the hash map if the value is none. Nevertheless, I think in this case, it is much better to just construct the hash map right after compiling the shader to avoid lack when you already started drawing. You can even get a list of the uniform attributes using get_program_parameter with ACTIVE_UNIFORMS and get_active_uniform. |
Can we make something like
glium
for Web GL? Does it make sense to jump straight there, or should we build an incremental Rustier version of the Web GL APIs before trying to make an Opinionated(tm) library?The text was updated successfully, but these errors were encountered: