WebGL2: uniform buffer demo crashes gpu driver on Windows |
||||||||
Issue descriptionUserAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2843.0 Safari/537.36 Steps to reproduce the problem: It looks like this only happens on specific configs, see about:gpu log below 1. enable WebGL2, otherwise the demo falls back to WebGL1 and runs correctly 2. on a Windows10 notebook with Optimis configuration with an Intel and NVIDIA GPU, go to the following URL: floooh.github.com/oryol-sticky-tests/BulletPhysicsBasic.html 3. note how the demo doesn't run but instead the browser becomes unresponsive for a few seconds and the GPU might 'reboot' (whole screen goes black sometimes, and comes back after a few seconds) 4. I can NOT reproduce this on my desktop PC with a Geforce 770 and Windows10, the demo runs well there. What is the expected behavior? The graphics driver shouldn't crash, even if the demo has faulty WebGL2 code (which I believe it doesn't) What went wrong? Graphics driver crashed. Crashed report ID: no crash report was recorded How much crashed? Just one tab Is it a problem with a plugin? No Did this work before? N/A Chrome version: 55.0.2843.0 Channel: canary OS Version: 10.0 Flash Version: Additional points: check in the text output field below the 3D canvas that WebGL2 / GLES3 is actually used, and that the string 'glRenderer: using cmdbuffer and global uniform block' appears. Otherwise this is the WebGL1 fallback. The demo runs without errors as native code with OpenGL3.3 core profile on the same machine, and on my Mac with Intel GPU. In Chrome Canary on the Mac, the following error is printed: ...uniform blocks are not backed by a buffer with sufficient data. Simpler uniform block demos are running, this demo is different because it binds uniform blocks to 4 different binding points, 2 for the vertex shader and 2 for the fragment shader, for different binding frequencies. The first binding only changes once per frame, the second binding per drawcall. All bindings point into the same big uniform buffer, but to different offsets in the global uniform buffer. The UB is updated per frame, and then glBindBufferRange is used to setup the offset into the uniform buffer. Here's the situation on different systems and browsers: - MacOS, Intel GPU, Firefox Nightly: works - MacOS, Intel GPU, Chrome Canary: prints error to JS console (uniform blocks are not backed by a buffer with sufficient data) - Win10, Optimus (Intel/NVIDIA), Chrome Canary: GPU driver crash - Win10, Optimus (Intel/NVIDIA), FF Nightly: black canvas, no JS console errors - Win10, NVIDIA GeForce 770, Chrome Canary: works - Win10, NVIDIA GeForce 770, FF Nightly: works I have attached the about:gpu page, since it didn't fit into this comment
,
Aug 30 2016
...and this time the proper URL, sorry: http://floooh.github.com/oryol-sticky-tests/BulletPhysicsBasic.html
,
Aug 30 2016
...I might have described the UB binding process wrong. I think the main difference to the simpler demos is that the same uniform buffer is bound to the vertex- and fragment-shader, both bindings are changed per draw call. There may be a separate per-frame binding happening on the vertex shader. But *all* uniform block bindings are pointing into the same big per-frame uniform buffer, and there are 2 of those which are alternated between frames. Before the first draw call, all uniform data for the frame is copied by a single glBufferSubData() call into the current per-frame uniform buffer.
,
Aug 30 2016
Link to Firefox ticket: https://bugzilla.mozilla.org/show_bug.cgi?id=1299099
,
Aug 31 2016
Thanks for the report. We'll try to find a machine on which this can be reproduced. Any chance you can zip up the test case and attach it to this bug report, in case it vanishes?
,
Aug 31 2016
Attacged is a zip with the test files (html, js, and .mem), just unzip into a directory where a web server is running and open the html file.
,
Aug 31 2016
,
Aug 31 2016
ANGLE or Intel folks: could someone please try to reproduce this? Please follow up and move the bug to Available state if it's reproducible. Thanks.
,
Aug 31 2016
Geoff can you take a look?
,
Aug 31 2016
I've been debugging a native iOS GLES3 version of the demo using the Xcode frame debugger, which allows to step through the rendering calls and provides detailed performance feedback and GL usage warnings. I'm getting a warning about a CPU stall in glBufferSubData() where the CPU needs to wait because the uniform buffer for the current frame is currently in use by the GPU (I messed that up somehow), but other then this stall-warning there are no other uniform buffer related warnings and especially no errors which would explain the "uniform blocks are not backed by a buffer with sufficient data" warning in Chrome on OSX. So I *think* that my code writes enough data to the uniform buffer. May be the stall-warning is a hint for what's happening in the browser version, not sure.
,
Aug 31 2016
BindBufferRange behaviors are different between OpenGL spec 4.1 and 4.2+. So basically for later GL (and also ES3), you can bind to a buffer range without the buffer having the size to back it up. But for 4.1, you have to have data store large enough for the BindBufferRange call. So we added some emulation code for MacOSX specific (because only MacOSX is running 4.1). Maybe that's why the behavior is different between Mac and Windows.
,
Sep 1 2016
I found the problem, at least for the warning I'm seeing on OSX (haven't tested Windows yet), but I'm not sure if and where this is written anywhere in the GL, GLES or WebGL spec: The last parameter of BindBufferRange (size) needs to be a multiple of 16. The demo runs correctly on Canary on OSX (without the buffer backing warning) if I'm rounding up the size to the next multiple of 16. Neither the BindBufferRange documentation nor the std140 layout documentation mention this as far as I can see. Attached is a new version with this fix. As I said, I haven't tested Windows yet. If this is indeed a valid requirement, the console error should be better, and it should be enforced in the WebGL spec, since the behaviour between FF and Chrome seems to differ at the moment.
,
Sep 1 2016
...unfortunately I'm still getting the driver crash on my Optimus Windows laptop, so it's 2 different things (one is the multiple-of-16 requirement for BindBufferRange size param, the other is the driver crash on some Windows configs).
,
Sep 1 2016
"BindBufferRange will generate an INVALID_VALUE error if index is greater than or equal to the value of MAX_UNIFORM_BUFFER_BINDINGS , or if offset is not a multiple of the implementation-dependent alignment requirement (the value of UNIFORM_BUFFER_OFFSET_ALIGNMENT )." OpenGL ES spec 3.0.4, page 71.
,
Sep 1 2016
Just looked at about:gpu - you're running on Intel. We don't support Intel on ES3 right this second, and there's an old bug 593024 that covers some existing problems with UBOs. I have a CL in progress (https://chromium-review.googlesource.com/#/c/368774/) but haven't tested to see if it fixes the crash. For now, switch to NVIDIA if you can, to develop WebGL 2 apps.
,
Sep 1 2016
,
Sep 1 2016
@zmo: I'm aligning the offset correctly according to UNIFORM_BUFFER_OFFSET_ALIGNMENT, and the binding slot number is smaller then MAX_UNIFORM_BUFFER_BINDING. The problem is that the size parameter must be a multiple of 16 (but only in Chrome Canary), which isn't mentioned anywhere (as far as I'm aware).
,
Sep 1 2016
I don't see any code in Chrome to require that. Might be a Mac driver thing. Can you provide a reduced test case for this?
,
Sep 1 2016
Ok, attached is a slightly modified version of this Oryol sample: http://floooh.github.io/oryol/asmjs/Shapes.html, but instead of only a single 4x4 ModelViewProj matrix in the vertex shader uniform block, it takes an additional float value, so that the VS uniform block size is 68 bytes (not a multiple of 16). On OSX in Canary, this triggers the error "[.Offscreen-For-WebGL-0x7fb3e5079800]GL ERROR :GL_INVALID_OPERATION : glDrawElements: uniform blocks are not backed by a buffer with sufficient data". There are 5 glBindBufferRange() calls per frame, 1 per draw call, with offsets 0, 256, etc... and the size parameter is 68. The same code runs fine as a native OSX OpenGL 4.1 Core Profile executable. Rounding the size parameter up to the next multiple of 16 (== 80) fixes the demo on Canary. The original demo, which takes a single 4x4 matrix works too, because 64 bytes is a multiple of 16. Rounding up to the next multiple of 8 produces the same error, and I didn't check numbers smaller than 4.
,
Sep 1 2016
PS: the per-frame glBufferSubData() call that happens before the first draw call in the frame to update the per-frame uniform buffer has a size parameter of 1280 (5 * 256), 5 is the number of uniform blocks in the buffer (one per draw call), and the 256 is coming from the offset alignment. The actual uniform block data in those 256-byte segments is 68 bytes (at least in the simplified Shapes demo).
,
Sep 1 2016
Thanks. I am taking a look on MacOSX.
,
Sep 2 2016
I know the issue on Mac. UniformBlock isn't always tightly packed. So you can't just add up each field size and allocate uniform buffer accordingly. You will need to call gl.getActiveUniformBlockParameter(gl.UNIFORM_BLOCK_DATA_SIZE) to know how much space you will need. The native app doesn't generate an GL error but the driver behavior in such situation (buffer size less than required) is undefined. In WebGL2 spec, we demand an INVALID_OPERATION to be generated to ensure consistent behavior across platforms/devices.
,
Sep 2 2016
Hmm I will try that (querying UNIFORM_BLOCK_DATA_SIZE), however, I am using std140 layout packing rules and as far as I can read from the spec the application shouldn't have to query UNIFORM_BLOCK_DATA_SIZE in that case, because the resulting uniform block size should be defined by std140 (as long as I'm following the padding rules of std140). I have a shader code generator which creates a matching C struct from shader uniform declarations, with padding as required by std140, and for the glBindBufferRange() I'm taking the sizeof() that struct. I'll see what I'm getting back from UNIFORM_BLOCK_DATA_SIZE and whether this matches my own structure layout. PS, here's the part from the spec I mean: "...The exception to this is the std140 uniform block layout, which guarantees specific packing behavior and does not require the application to query for offsets and strides. In this case the minimum size may still be queried, even though it is determined in advance based only on the uniform block declaration (see “Standard Uniform Block Layout” in section 2.11.6)."
,
Sep 2 2016
Ok tested and you are right, and it seems I stumbled over the last part of the std140 spec. Both on OSX and Windows I'm getting 80 back for UNIFORM_BLOCK_DATA_SIZE for the following uniform block:
layout (std140) uniform params {
mat4 mvp;
float bla;
};
And I think the relevant part in the std140 spec is this (the very last sentence):
For uniform blocks laid out according to these rules, the minimum buffer
object size returned by the UNIFORM_BLOCK_DATA_SIZE query is derived by
taking the offset of the last basic machine unit consumed by the last
uniform of the uniform block (including any end-of-array or
end-of-structure padding), adding one, and rounding up to the next
multiple of the base alignment required for a vec4.
As a side note, would it be possible to provide better and more precise validation errors around uniform buffers?
I bet a lot of WebGL2 programmers will stumble over details like this. I mean I do GL coding for 10 years, and only learn about this now because other platforms don't enforce the spec as much as WebGL ;)
I'll update my code according to this, and report back, I'm interested whether this changes anything for the crash on Windows I'm seeing.
,
Sep 2 2016
Ok, I have uploaded a new version of the original physics demo under a new URL, which uses the size value queried from GL_UNIFORM_BLOCK_DATA_SIZE for the call to glBindBufferRange(), and this fixes the demo on OSX. No change of behaviour on Windows, but that's kinda expected (because known issues with Intel GPUs). http://floooh.github.io/oryol-sticky-tests/new/BulletPhysicsBasic.html I'll update the Firefox ticket, I think they should have a similar validation check for passing the wrong uniform block size to glBindBufferRange(), since the underlying GL implementations don't seem to care. My only remaining wish would be more detailed error messages, crazy idea but it would be cool if the error messages would link to the related places in the WebGL2 spec. Other than that I think the only remaining issue is the crash on Intel GPUs on Windows, which is most likely ANGLE related. The natively compiled GL demo is running fine on my Optimus setup on the Intel GPU. Thanks and sorry for the bother ;)
,
Sep 2 2016
Yes, after going through this debugging process with your case, we do realize a better error message will help developers. We plan to include the required buffer size / actual buffer size in the error message - hopefully this will give developers enough info where their code goes wrong. Really appreciate your help in reporting the issue and providing small test cases - this leads to better WebGL2 implementations.
,
Dec 2 2016
|
||||||||
►
Sign in to add a comment |
||||||||
Comment 1 Deleted