Skip to content
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

WebGL bindBuffer & bufferSubData errors/corruption when using Tess::indices_mut() #483

Open
kpreid opened this issue Feb 14, 2021 · 15 comments · Fixed by #506
Open

WebGL bindBuffer & bufferSubData errors/corruption when using Tess::indices_mut() #483

kpreid opened this issue Feb 14, 2021 · 15 comments · Fixed by #506
Labels
bug:gpu-state A bug that makes the GPU state go invalid.

Comments

@kpreid
Copy link
Contributor

kpreid commented Feb 14, 2021

I'm noticing a problem where intermittently, in some of my Tesses, when their index storage has been updated using indices_mut(), some of the indices will be seemingly pointing to a vertex with zero data (causing triangles missing or stretched to the world origin):

Screen Shot 2021-02-14 at 12 32 48

and errors including the following will appear in the Chrome console:

WebGL: INVALID_OPERATION: bindBuffer: element array buffers can not be bound to a different target
WebGL: INVALID_OPERATION: bufferSubData: no buffer
WebGL: INVALID_VALUE: bufferSubData: buffer overflow

If I modify luminance-examples-web/src/lib.rs to contain the line

let mut im = indexed_triangles.indices_mut().unwrap();

inside render_scene() (with accompanying changes to have the necessary mutable reference), then I get no rendering glitches but I do see this error and stack:

index_bg.js:390 WebGL: INVALID_OPERATION: bindBuffer: element array buffers can not be bound to a different target

__wbg_bindBuffer
web_sys::features::gen_WebGl2RenderingContext::WebGl2RenderingContext::bind_buffer
luminance_webgl::webgl2::state::WebGL2State::bind_array_buffer
luminance_webgl::webgl2::buffer::update_webgl_buffer
<luminance_webgl::webgl2::buffer::BufferSliceMutWrapper as core::ops::drop::Drop>::drop
core::ptr::drop_in_place
core::ptr::drop_in_place
core::ptr::drop_in_place
luminance_examples_web::render_scene

This suggests that BufferSliceMutWrapper::drop is somehow not correctly managing the GL state, such that bindBuffer fails and the following bufferSubData then overwrites an unrelated buffer from the previous binding.

@hadronized hadronized added the bug:gpu-state A bug that makes the GPU state go invalid. label Feb 15, 2021
kpreid added a commit to kpreid/all-is-cubes that referenced this issue Feb 15, 2021
hadronized added a commit that referenced this issue May 3, 2021
In the OpenGL 3.3 backend, there is an implementation that allows a
buffer object to be created and bound to a given target (for instance
`ARRAY_BUFFER`) and allows to rebind the buffer later to another
target (for instance `ELEMENT_ARRAY_BUFFER`). That optimization is handy
because it allows to dissociate data storage from data representation,
allowing to send a bunch of bytes and then using the target as source of
representation.

Well, turns out that optimization doesn’t work in WebGL2, so this commit
implements a way to keep track of what a buffer was created as and then
re-use that information every time we need to bind the buffer. That
should fix #483 (and probably other bugs no one has encountered yet).

Some integration tests are needed to ensure the fix does what it should.
@hadronized
Copy link
Owner

@kpreid Hey, do you think you can test that patch #506 and tell me how it’s running? I’m going to write some integration test about that when I have time.

hadronized added a commit that referenced this issue May 3, 2021
In the OpenGL 3.3 backend, there is an optimization that allows a
buffer object to be created and bound to a given target (for instance
`ARRAY_BUFFER`) and allows to rebind the buffer later to another
target (for instance `ELEMENT_ARRAY_BUFFER`). That optimization is handy
because it allows to dissociate data storage from data representation,
allowing to send a bunch of bytes and then using the target as source of
representation.

Well, turns out that optimization doesn’t work in WebGL2, so this commit
implements a way to keep track of what a buffer was created as and then
re-use that information every time we need to bind the buffer. That
should fix #483 (and probably other bugs no one has encountered yet).

Some integration tests are needed to ensure the fix does what it should.
hadronized added a commit that referenced this issue May 3, 2021
In the OpenGL 3.3 backend, there is an optimization that allows a
buffer object to be created and bound to a given target (for instance
`ARRAY_BUFFER`) and allows to rebind the buffer later to another
target (for instance `ELEMENT_ARRAY_BUFFER`). That optimization is handy
because it allows to dissociate data storage from data representation,
allowing to send a bunch of bytes and then using the target as source of
representation.

Well, turns out that optimization doesn’t work in WebGL2, so this commit
implements a way to keep track of what a buffer was created as and then
re-use that information every time we need to bind the buffer. That
should fix #483 (and probably other bugs no one has encountered yet).

Some integration tests are needed to ensure the fix does what it should.
@kpreid
Copy link
Contributor Author

kpreid commented May 4, 2021

@kpreid Hey, do you think you can test that patch #506 and tell me how it’s running? I’m going to write some integration test about that when I have time.

I prepared to test but ran into a compilation error that appears to be due to b607b1e not having updated the WebGL2 backend, and left a comment on #477 about it.

hadronized added a commit that referenced this issue May 4, 2021
hadronized added a commit that referenced this issue May 4, 2021
hadronized added a commit that referenced this issue May 4, 2021
hadronized added a commit that referenced this issue May 4, 2021
hadronized added a commit that referenced this issue May 4, 2021
In the OpenGL 3.3 backend, there is an optimization that allows a
buffer object to be created and bound to a given target (for instance
`ARRAY_BUFFER`) and allows to rebind the buffer later to another
target (for instance `ELEMENT_ARRAY_BUFFER`). That optimization is handy
because it allows to dissociate data storage from data representation,
allowing to send a bunch of bytes and then using the target as source of
representation.

Well, turns out that optimization doesn’t work in WebGL2, so this commit
implements a way to keep track of what a buffer was created as and then
re-use that information every time we need to bind the buffer. That
should fix #483 (and probably other bugs no one has encountered yet).

Some integration tests are needed to ensure the fix does what it should.
@hadronized
Copy link
Owner

That should be okay now @kpreid. :)

@kpreid
Copy link
Contributor Author

kpreid commented May 4, 2021

Updated test report against 0bb49ae: there is no visual corruption, but the index buffer updates are not taking effect, and there are errors logged: apparently one for the vertex buffer mutation and one for the index buffer mutation (two different code paths that were both disabled to avoid this bug):

WebGL: INVALID_OPERATION: bindBuffer: element array buffers can not be bound to a different target
web_sys::features::gen_WebGl2RenderingContext::WebGl2RenderingContext::bind_buffer
luminance_webgl::webgl2::state::WebGL2State::bind_array_buffer
<luminance_webgl::webgl2::buffer::BufferSliceMutWrapper as core::ops::drop::Drop>::drop
all_is_cubes::lum::space::SpaceRenderer::prepare_frame
WebGL: INVALID_OPERATION: bufferSubData: no buffer
web_sys::features::gen_WebGl2RenderingContext::WebGl2RenderingContext::buffer_sub_data_with_i32_and_u8_array_and_src_offset
<luminance_webgl::webgl2::buffer::BufferSliceMutWrapper as core::ops::drop::Drop>::drop
core::ptr::drop_in_place<luminance::tess::IndicesMut<luminance_webgl::webgl2::WebGL2,all_is_cubes::lum::types::LumBlockVertex,u32,(),luminance::tess::Interleaved>>
all_is_cubes::lum::space::Chunk::depth_sort_for_view

@hadronized
Copy link
Owner

I am going to rework the examples so that I can share them between all platforms. I have an application I’ve been playing with as an experiment already doing that so that should be rather easy. That should allow to showcase that kind of bug because right now I don’t have a reproducible way to do it but I think the interactive triangle example should be a good start.

hadronized added a commit that referenced this issue Jun 22, 2021
In the OpenGL 3.3 backend, there is an optimization that allows a
buffer object to be created and bound to a given target (for instance
`ARRAY_BUFFER`) and allows to rebind the buffer later to another
target (for instance `ELEMENT_ARRAY_BUFFER`). That optimization is handy
because it allows to dissociate data storage from data representation,
allowing to send a bunch of bytes and then using the target as source of
representation.

Well, turns out that optimization doesn’t work in WebGL2, so this commit
implements a way to keep track of what a buffer was created as and then
re-use that information every time we need to bind the buffer. That
should fix #483 (and probably other bugs no one has encountered yet).

Some integration tests are needed to ensure the fix does what it should.
hadronized added a commit that referenced this issue Jun 27, 2021
In the OpenGL 3.3 backend, there is an optimization that allows a
buffer object to be created and bound to a given target (for instance
`ARRAY_BUFFER`) and allows to rebind the buffer later to another
target (for instance `ELEMENT_ARRAY_BUFFER`). That optimization is handy
because it allows to dissociate data storage from data representation,
allowing to send a bunch of bytes and then using the target as source of
representation.

Well, turns out that optimization doesn’t work in WebGL2, so this commit
implements a way to keep track of what a buffer was created as and then
re-use that information every time we need to bind the buffer. That
should fix #483 (and probably other bugs no one has encountered yet).

Some integration tests are needed to ensure the fix does what it should.
hadronized added a commit that referenced this issue Jun 28, 2021
In the OpenGL 3.3 backend, there is an optimization that allows a
buffer object to be created and bound to a given target (for instance
`ARRAY_BUFFER`) and allows to rebind the buffer later to another
target (for instance `ELEMENT_ARRAY_BUFFER`). That optimization is handy
because it allows to dissociate data storage from data representation,
allowing to send a bunch of bytes and then using the target as source of
representation.

Well, turns out that optimization doesn’t work in WebGL2, so this commit
implements a way to keep track of what a buffer was created as and then
re-use that information every time we need to bind the buffer. That
should fix #483 (and probably other bugs no one has encountered yet).

Some integration tests are needed to ensure the fix does what it should.
hadronized added a commit that referenced this issue Jun 28, 2021
This relates to #483 for testing corruption of `indices_mut()`. The
example showcases the bug when used on the `web` executor (no issue with
the `desktop` one).

This bug is fixed in the next commit.
hadronized added a commit that referenced this issue Jun 28, 2021
The bug was due to still using `bind_array_buffer` when streaming buffer
updates to the GPU.

Relates to #483.
@hadronized
Copy link
Owner

Okay, I think I finally went through this bug. The bug by itself was pretty easy to spot and fix but the infrastructure to enabling functional testing and examples took me quite a while (and a ride). @kpreid do you mean testing again #506 ? :)

@hadronized hadronized added the awaiting-reporter We need validation and/or more information from the issue reporter. label Jun 28, 2021
@kpreid
Copy link
Contributor Author

kpreid commented Jun 29, 2021

Still not fully working. Tested against e3c1628 : errors and visual corruption. There are fewer errors reported now; Chrome says only this (no stack trace):

[.WebGL-0x7fe5f1045a00]GL ERROR :GL_INVALID_OPERATION : glDrawElements: range out of bounds for buffer

Firefox says, also with no stack trace:

WebGL warning: drawElementsInstanced: Index buffer too small.

My relevant draw calls are like tess_gate.render(tess.view(range).unwrap())?; — I'm passing an explicit range, and that's not failing, so it isn't that luminance is tracking the number of elements to draw incorrectly, but the buffer GL thinks it should be drawing is not the size luminance expects it to be.

I've uploaded my patches to test against yours at https://github.com/kpreid/all-is-cubes/tree/lum-webgl-fix.

@hadronized
Copy link
Owner

I tried running your code but the application (WASM) doesn’t seem to initialize correctly.

Error: did not initialize renderer: graphics error (in ?): shader program warning: uniform warning: inactive view_position uniform
    __wbg_new_3e1ee746fe308c9f webpack://all-is-cubes/./pkg/index_bg.js?:1128
    logError webpack://all-is-cubes/./pkg/index_bg.js?:394
    __wbg_new_3e1ee746fe308c9f webpack://all-is-cubes/./pkg/index_bg.js?:1127
    __wbg_new_3e1ee746fe308c9f http://localhost:8080/index.js:1300
    start_game webpack://all-is-cubes/./pkg/index_bg.js?:451
    bootstrap webpack://all-is-cubes/./js/bootstrap.js?:16
    <anonymous> webpack://all-is-cubes/./js/index.js?:8

@kpreid
Copy link
Contributor Author

kpreid commented Jun 30, 2021

I've pushed a patch with #[uniform(unbound)] for that.

It seems that different OpenGL implementations have different ideas of how much dead code analysis to do before declaring a uniform inactive, and macOS is less thorough here than (some) Linux. (I was, in one case, computing a value in the vertex shader to pass to the fragment shader, then ignoring it in the fragment shader.)

@kpreid
Copy link
Contributor Author

kpreid commented Jul 23, 2021

I just noticed this is still tagged awaiting-reporter. Is there any more info you need?

hadronized added a commit that referenced this issue Sep 5, 2021
In the OpenGL 3.3 backend, there is an optimization that allows a
buffer object to be created and bound to a given target (for instance
`ARRAY_BUFFER`) and allows to rebind the buffer later to another
target (for instance `ELEMENT_ARRAY_BUFFER`). That optimization is handy
because it allows to dissociate data storage from data representation,
allowing to send a bunch of bytes and then using the target as source of
representation.

Well, turns out that optimization doesn’t work in WebGL2, so this commit
implements a way to keep track of what a buffer was created as and then
re-use that information every time we need to bind the buffer. That
should fix #483 (and probably other bugs no one has encountered yet).

Some integration tests are needed to ensure the fix does what it should.
hadronized added a commit that referenced this issue Sep 5, 2021
This relates to #483 for testing corruption of `indices_mut()`. The
example showcases the bug when used on the `web` executor (no issue with
the `desktop` one).

This bug is fixed in the next commit.
hadronized added a commit that referenced this issue Sep 5, 2021
The bug was due to still using `bind_array_buffer` when streaming buffer
updates to the GPU.

Relates to #483.
@hadronized hadronized removed the awaiting-reporter We need validation and/or more information from the issue reporter. label Sep 6, 2021
@hadronized
Copy link
Owner

I’m going to try to fix that but the repository you linked doesn’t build so I can’t go any further. I will merge the PR as it’s already fixing things and will try with your code later as I can’t make it work and can’t reproduce then.

@hadronized
Copy link
Owner

Also, I think I’ve found where the problem comes from. Creating a new buffer (here) in WebGL will always use the same target which is not going to authorize element arrays to be created correctly.

@kpreid
Copy link
Contributor Author

kpreid commented Sep 6, 2021

I tested with commit ba6d169 and still got buffer errors.

I'd like to work with you on figuring out why my code won't build for you — I know it'll build on macOS and Linux (and the GitHub CI servers) but maybe I forgot to document some prerequisite or something like that. Feel free to open an issue on https://github.com/kpreid/all-is-cubes/issues or contact me through Discord.

Of course, another option is to reduce it to a smaller project, but it'd still have to have a wasm build. It should be possible to have a repro that fits into luminance-examples-web…

@kpreid
Copy link
Contributor Author

kpreid commented Sep 11, 2021

There are less frequent errors but still errors/corruption (tested at commit 11b0e62 on master). Based on the comment in the PR ("Merging this as it’s already a fix, and I will continue fixing the buffer problems by redesigning them a bit.") I think this issue should still be open?

@hadronized
Copy link
Owner

It shouldn’t have been closed, sorry for that.

@hadronized hadronized reopened this Sep 12, 2021
hadronized added a commit that referenced this issue Sep 12, 2021
Buffers are not really needed / used by people and are an implementation
detail, as they are required for (currently) three things:

- Vertex buffers (for `Tess`).
- Index buffers (still for `Tess`).
- And uniform buffers, which are known to be problematic because of the
  unsafe nature of using them and will require a redesign.

So better get rid of the concept of buffers and expose directly the
upper abstractions, such as slicing `Tess`, which is already supported
and then won’t be a breaking change. The only case that breaks is when
people want to build a `Tess` from GPU buffers, which is not going to be
supported anymore.

Relates to #483.
hadronized added a commit that referenced this issue Sep 12, 2021
Buffers are not really needed / used by people and are an implementation
detail, as they are required for (currently) three things:

- Vertex buffers (for `Tess`).
- Index buffers (still for `Tess`).
- And uniform buffers, which are known to be problematic because of the
  unsafe nature of using them and will require a redesign.

So better get rid of the concept of buffers and expose directly the
upper abstractions, such as slicing `Tess`, which is already supported
and then won’t be a breaking change. The only case that breaks is when
people want to build a `Tess` from GPU buffers, which is not going to be
supported anymore.

Relates to #483.
hadronized added a commit that referenced this issue Sep 12, 2021
Buffers are not really needed / used by people and are an implementation
detail, as they are required for (currently) three things:

- Vertex buffers (for `Tess`).
- Index buffers (still for `Tess`).
- And uniform buffers, which are known to be problematic because of the
  unsafe nature of using them and will require a redesign.

So better get rid of the concept of buffers and expose directly the
upper abstractions, such as slicing `Tess`, which is already supported
and then won’t be a breaking change. The only case that breaks is when
people want to build a `Tess` from GPU buffers, which is not going to be
supported anymore.

Relates to #483.
hadronized added a commit that referenced this issue Sep 16, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug:gpu-state A bug that makes the GPU state go invalid.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants