-
-
Notifications
You must be signed in to change notification settings - Fork 3.8k
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
[Merged by Bors] - make pbr shader std140 compatible #1798
Conversation
@@ -38,10 +38,8 @@ const int MAX_LIGHTS = 10; | |||
|
|||
struct Light { | |||
mat4 proj; | |||
vec3 pos; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems this change may be reverted (memory layout is the same). I've tested that in bevy_webgl2 and it seems to work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, in std140
memory layout is the same between vec3
and vec4
. That's why, from what I read, vec3
should be avoided to not make mistake when reading the code to think that the layout is different
@@ -57,12 +55,12 @@ layout(location = 0) out vec4 o_Target; | |||
layout(set = 0, binding = 0) uniform CameraViewProj { | |||
mat4 ViewProj; | |||
}; | |||
layout(set = 0, binding = 1) uniform CameraPosition { | |||
vec3 CameraPos; | |||
layout(std140, set = 0, binding = 1) uniform CameraPosition { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems adding std140 only is sufficient:
layout(std140, set = 0, binding = 1) uniform CameraPosition {
vec3 CameraPos;
}
}; | ||
|
||
layout(set = 1, binding = 0) uniform Lights { | ||
vec3 AmbientColor; | ||
layout(std140, set = 1, binding = 0) uniform Lights { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the same
I think im in agreement with the idea to just avoid vec3 in this context. Better to use the least surprising type. |
(we've hit this exact issue in the past) |
bors r+ |
In shaders, `vec3` should be avoided for `std140` layout, as they take the size of a `vec4` and won't support manual padding by adding an additional `float`. This change is needed for 3D to work in WebGL2. With it, I get PBR to render <img width="1407" alt="Screenshot 2021-04-02 at 02 57 14" src="https://user-images.githubusercontent.com/8672791/113368551-5a3c2780-935f-11eb-8c8d-e9ba65b5ee98.png"> Without it, nothing renders... @cart Could this be considered for 0.5 release? Also, I learned shaders 2 days ago, so don't hesitate to correct any issue or misunderstanding I may have bevy_webgl2 PR in progress for Bevy 0.5 is here if you want to test: https://github.com/rparrett/bevy_webgl2/pull/1
Pull request successfully merged into main. Build succeeded: |
Avoiding vec3 is simply not necessary. Using std140 correctly on both sides, GPU and CPU, solves this problem and allows Vec3's to just be vec3's, etc. |
Hmm yeah is this a fair assessment of the situation? It seems like we have three options:
None of these seem fun 😄 |
I have been working on a crevice PR in #1931. The biggest problem I ran into is the orphan rule and having to wrap any external types that need crevice traits (or doing PRs, but that's not going to fly for many things). I also spent a few hours yesterday looking into the possibilities of using serde for Std140 because of:
On the other hand, it's not a dedicated solution and alignment of arrays and structs in particular is a bit of a challenge with how serde passes struct fields to the serializer in order, whereas the first element (of an array or struct) needs to be aligned according to the contents of the array or struct. In the case of arrays we have the information we need when the first element is passed, but for structs we might need to make a copy, because we need the largest alignment of any member. |
I agree that this needed merging, to fix the current issue. But if we're going to do manual padding anyway, it doesn't matter at all whether we avoid vec3 or not, as long as the padding is correct and the uniforms are marked std140 (which they should anyway, because otherwise the API is free to reorder fields). Instead of arbitrarily avoiding vec3, and there's actually a few more rules, we should implement a general solution that does not require users to be aware of these details. In my ideal world, I think we track uniform types for all compiled shaders, so that we can:
Since we really need shader includes anyway, as our shaders grow, it actually makes sense to make this part of the solution. Instead of specifying uniforms once in rust and once in glsl, we should really have a single definition that is reusable on the other side. Whether that definition is rust and glsl is generated or the other way around. |
I really don't like the extra boilerplate of requiring std140 everywhere. Its one more thing to remember (and users likely won't include it). Could you elaborate a bit on (or link to) the implications of the "the api is free to reorder fields"? If we really need to include std140 everywhere to ensure predictable behavior, we should look into ways to make this automatic and implicit (ex: flip a bit in the shader compiler or add a post-processing step). Alternatively, we could look into the WGSL memory layout situation and consider moving to that if it is more reasonable.
I'm not a huge fan of codegen here as it complicates the build process and introduces its own complexity (in terms of importing the generated items). I would personally prefer a macro that handles proper memory layout and solid validation layers that yell when something doesn't line up. However there is some prior art here. Rafx has optional codegen: https://github.com/aclysma/rafx/blob/master/docs/shaders/shader_processor.md. |
This is because, technically, you have to query the API for the field offsets of the uniform. By specifying std140 you force it to adhere to a single layout. This is also likely why issues showed up for webgl2 and DX12 that didn't for vulkan. Read https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Memory_layout and weep.
As of last June, there wasn't agreement on a single layout, see https://github.com/gfx-rs/wgpu-rs/issues/349#issuecomment-638873319. Maybe this has changed, but I can't find reference to it, so we should probably ask Groves or kvark.
That works, I mean, one can actually query the offsets from the API. But what I mostly mean is we need to make this easier on users. Because there's going to be way more issues once we start having multiple shaders specifying the same uniforms. Because, as we've seen, there's 0 useful errors from them being wrong. Which is why something like filament uses includes (of generated uniforms, as well, but that's not needed) to be sure of uniformity (pun intended). |
In shaders,
vec3
should be avoided forstd140
layout, as they take the size of avec4
and won't support manual padding by adding an additionalfloat
.This change is needed for 3D to work in WebGL2. With it, I get PBR to render

Without it, nothing renders... @cart Could this be considered for 0.5 release?
Also, I learned shaders 2 days ago, so don't hesitate to correct any issue or misunderstanding I may have
bevy_webgl2 PR in progress for Bevy 0.5 is here if you want to test: https://github.com/rparrett/bevy_webgl2/pull/1