-
Notifications
You must be signed in to change notification settings - Fork 953
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
Implement WebGL2 Backend #1686
Implement WebGL2 Backend #1686
Conversation
d5aab51
to
8667453
Compare
@cwfitzgerald Sorry I had to drop off of chat, but I've got another issue. When Naga compiles the shadow shader for GLSL ES, it includes certain features that aren't supported such as an array without a length. While that array isn't used, because we were going to use a different entrypoint, it doesn't matter because the GLSL won't compile. I'm not seeing a way around needing a shader preprocessor. Something like I could trivially implement one with whatever syntax we want if you don't mind pulling in 1 dependency ( not counting What do you think? |
Where is this failing and how? Shadow example works on GLES backend generally on native. When Naga's GLSL backend is invoked, it's given the exact entry point. And it already knows from the IR what global variables are used by this entry point. So technically it could be smart enough to avoid emitting the definition of this structure entirely if a different entry point is chosen. |
It fails on WebGL when uncommenting this line in the shader: [[block]]
struct Lights {
data: [[stride(96)]] array<Light>;
} It works fine on Native OpenGL.
Ah, that would be great. I could look into doing a PR for that, then. |
Now we've got bunnymark and shadow exeamples working. The water example is almost working, but the water is not appearing, and I can narrow the issue down to a pixel depth calculation failing. If I take out the step in the shader that uses the that depth, it appears fine minus the slight bluring around the edges of the water that isn't there: Here's the portion of the shader in question: water.wgsl: // I think the issue's in here
fn to_linear_depth(depth: f32) -> f32 {
let z_n: f32 = 2.0 * depth - 1.0;
let z_e: f32 = 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear));
return z_e;
}
[[stage(fragment)]]
fn fs_main(in: VertexOutput) -> [[location(0)]] vec4<f32> {
let reflection_colour = textureSample(reflection, colour_sampler, in.f_WaterScreenPos.xy).xyz;
let pixel_depth = to_linear_depth(in.position.z);
let normalized_coords = in.position.xy / vec2<f32>(uniforms.time_size_width.w, uniforms.viewport_height);
let terrain_depth = to_linear_depth(textureSample(terrain_depth_tex, colour_sampler, normalized_coords).r);
// This pixel depth here is the issue. If set to zero the water is visible
let dist = terrain_depth - pixel_depth;
let clamped = pow(smoothStep(0.0, 1.5, dist), 4.8);
let final_colour = in.f_Light + reflection_colour;
let t = smoothStep(1.0, 5.0, dist) * 0.2; //TODO: splat for mix()?
let depth_colour = mix(final_colour, water_colour, vec3<f32>(t, t, t));
return vec4<f32>(depth_colour, clamped * (1.0 - in.f_Fresnel));
} generated glsl for: float to_linear_depth(float depth) {
float z_n = ((2.0 * depth) - 1.0);
float z_e = (((2.0 * 10.0) * 400.0) / ((400.0 + 10.0) - (z_n * (400.0 - 10.0))));
return z_e;
}
void main() {
VertexOutput in1 = VertexOutput(gl_FragCoord, _vs2fs_location0, _vs2fs_location1, _vs2fs_location2);
vec4 _expr16 = texture(_group_0_binding_1, vec2(in1.f_WaterScreenPos.xy));
vec3 reflection_colour = _expr16.xyz;
float _expr20 = to_linear_depth(in1.position.z);
vec4 _expr24 = _group_0_binding_0.time_size_width;
float _expr27 = _group_0_binding_0.viewport_height;
vec2 normalized_coords = (in1.position.xy / vec2(_expr24.w, _expr27));
vec4 _expr30 = texture(_group_0_binding_2, vec2(normalized_coords));
float _expr32 = to_linear_depth(_expr30.x);
float dist = (_expr32 - _expr20);
float clamped = pow(smoothstep(0.0, 1.5, dist), 4.8);
vec3 final_colour = (in1.f_Light + reflection_colour);
float t = (smoothstep(1.0, 5.0, dist) * 0.2);
vec3 depth_colour = mix(final_colour, vec3(0.0, 0.46, 0.95), vec3(t, t, t));
_fs2p_location0 = vec4(depth_colour, (clamped * (1.0 - in1.f_Fresnel)));
return;
} Getting close! |
For platforms that don't support Read-only depth stencil like OpenGL/WebGL (we should have a downlevel limit for this) we can do an explicit copy-texture-to-texture to another depth buffer so we don't get a cycle of textures. |
In addition to @cwfitzgerald , this appears to be UB according to WebGL spec. I see that it works at least on some OpenGL ES devices, so I thought we may get away without another downlevel flag. Looks like WebGL is more strict about this, although I'd expect to see an actual error. |
9c3b6a4
to
8c434e1
Compare
1737: Use Bitshift Style for All Bitflag Consts r=cwfitzgerald a=zicklag **Connections** None exactly, but somewhat related to #1686 as I was going to put some of this change into that PR, but am splitting it out for ease of review. **Description** This is purely code-style related, but it makes it easier to read bitflag constants and much eaiser to add bitflags in the future. **Testing** Tested on Ubuntu 20.04 Co-authored-by: Zicklag <zicklag@katharostech.com>
👀 |
Whoops! That was an accident. I actually just pushed an update. :) |
aafb727
to
b6d7a87
Compare
d4dd43f
to
5797c12
Compare
Filed #2031 to track the last unresolved thing |
Perfect. I also opened #2032 to track the water example issue. Do we want a separate issue for WebGL lint issues? |
Sure, and thank you! |
@zicklag this doesn't support |
|
@zicklag @anlumo We might consider working on a PR for supporting WebGL2 with OffscreenCanvas. Do you have an idea how difficult this might be or how to get started? |
It might have already been implemented here: #1932. |
Oh, I'm not sure then. You might be right. I don't remember what the state was in, and I haven't been following progress so I'm not sure where it's at now, but my gut reaction is that your initial comment above seemed correct about how you might go about it, and that it probably wouldn't be super difficult to get working. |
It looks like this shouldn't be very difficult, but one challenge is that the WebGPU function Is it possible to check if the current backend is GLES and directly access a function that is not part of the HAL API? Any ideas welcome 😊 |
OK, yeah, I'm looking a little deeper and it looks like this should be easy. So right after this struct I think you want to create a new You want to implement this similar to the Then you'll need to create a function like this one with the You'll need to do something similar to that function where it calls an instance function with Next you need to add a function like this one that calls the function you just wrote above. ( Again we're following the pattern of how the metal backend allows you to create surfaces directly. ) Finally you create one more function like this one to finish it off and expose the function for creating a surface from the canvas in the public API of I may have gotten something wrong, so hopefully that works or at least gives you direction. :) |
The type definition is: typedef (OffscreenCanvasRenderingContext2D or ImageBitmapRenderingContext or WebGLRenderingContext or WebGL2RenderingContext or GPUCanvasContext) OffscreenRenderingContext; So the type itself is the same, but the biggest difference is that OffscreenCanvas is available in web workers. You don't have a DOM in web workers, so you can't create a regular canvas there. |
Thanks a lot @zicklag for the great outline how start with the OffscreenCanvas feature. 🙏
Yes, it's separate and is not part of the HTML DOM. |
There's also transferControlToOffscreen, which allows you to move the renderer to a web worker, but still render directly to the screen. This avoids blocking the UI thread. |
Cool, thanks for sharing this approach @anlumo! |
Connections
Works towards #1617 .
Closes #1740.
Description
Migrates the WebGL2 backend from
gfx-hal-backend
towgpu-hal
.Testing
Tested WebGL ( Brave Browser ( Chrome fork ), and Firefox ) and GL backends on Ubuntu 20.04 in.
Currently only the cube, triangle, and msaa-line examples work. This is still work-in-progress, with some things still to be done, but I wanted to get the code up here, and there isn't a lot that I think will changing drastically.
Remaining Work
adapter.rs:183
)