emscripten-core / emscripten

Emscripten: An LLVM-to-WebAssembly Compiler
Other
25.8k stars 3.31k forks source link

Immediate mode OpenGL and client attributes #1221

Closed AkhRani closed 11 years ago

AkhRani commented 11 years ago

Some background: I have been experimenting with adding occlusion query support to the WebGL api in Chrome, to see what effect it has on BananaBread performance. I am pretty sure I have the bindings working correctly, but I'm running into what I think are problems in the emscripten GL.immediate object.

When occlusion queries are used, Sauerbraten can detect that a certain chunk of geometry was not rendered in frame N-1. Then in frame N, instead of drawing the actual chunk of geometry, it draws a bounding box (see drawbb() in renderva.cpp). This bounding box consists of a glBegin (QUAD) / glEnd block, with glVertex3f calls in the middle. Chunks of geometry that were visible in the previous frame are drawn normally.

Initially, this caused an assertion failure (assert(numVertexes%1 == 0);) in GL.immediate.flush(). This is because when prepareClientAttributes is called from glEnd, both TEXTURE0 and VERTEX are enabled in GL.immediate.enabledClientAttributes, so the stride is calculated as 20. However, the Begin/End block only includes vertexes coordinates, and not texture coordinates, so the stride should be 12.

I have tried two workarounds. First, I modified glVertex3f, so that if TEXTURE0 was enabled, but no new texture coordinates had been specified, the most recently specified texture coordinates would be used. I believe this is what a normal OpenGL system would do.

Second, I modified prepareClientAttributes so that if beginEnd is true, an attribute must be present in both GL.immediate.enabledClientAttributes and GL.immediate.rendererComponents in order to be pushed into attributes.

On the plus side, both of these changes stopped the crash. Unfortunately, with both of these changes, most of the scene is not rendered at all. The HUD (Health, armor, ammo) is displayed, along with the player's weapon, and the red effect appears when the player takes damage, but that's it.

I'm far from being an OpenGL expert, so I'm somewhat out of my depth here. But it seems like there is some sort of issue when immediate-mode drawing blocks are interleaved with more modern drawing methods. Any guidance you can give for how to solve this problem would be greatly appreciated.

I am planning to turn on the debugging in gl_drawframe, and to capture the output with and without occlusion query support. Is there anything else you would like to see?

kripken commented 11 years ago

Hmm, I am not a GL expert either ;) but that sounds possible. Although I am surprised since we have Sauerbraten working already - is it using a completely new code path when occlusion queries are enabled?

If you can make a standalone testcase showing the issue, I can help debug that. That's the approach I took for getting Sauerbraten ported, basically finding stuff that didn't work, making a testcase, and figuring out what to do to fix that (see test/*cubegeom* in emscripten).

AkhRani commented 11 years ago

I think it only draws bounding boxes when occlusion queries are enabled. (Sauerbraten seems to have lots of conditional code, to use features when available, but work OK when they are not.) I'll see if I can make a test case.

On Thu, May 30, 2013 at 5:24 PM, Alon Zakai notifications@github.comwrote:

Hmm, I am not a GL expert either ;) but that sounds possible. Although I am surprised since we have Sauerbraten working already - is it using a completely new code path when occlusion queries are enabled?

If you can make a standalone testcase showing the issue, I can help debug that. That's the approach I took for getting Sauerbraten ported, basically finding stuff that didn't work, making a testcase, and figuring out what to do to fix that (see test/cube in emscripten).

— Reply to this email directly or view it on GitHubhttps://github.com/kripken/emscripten/issues/1221#issuecomment-18716863 .

AkhRani commented 11 years ago

The first couple of cubegeom tests fail for me. I haven't tried the others. I'm using mingw64 under Windows, with the prebuilt binaries listed in the wiki. I tried both Firefox and Chrome, and both seemed to run the test to completion, with no assertion failures, but got the wrong checksum. The bad checksum is consistent between Firefox and Chrome, and it differs between the first cubegeom test and the second. The browser screen shows something that looks 3d-ish during the test.

[edit] I built cubegeom.c natively. I had to comment out calls to glGetPointerv and the assert(lightmapLocation != glGetUniformLocation(program, "lightmap")), and move the calls to glReadPixels before the call to swap buffers, and the checksum does not match any of the expected values in the unit test.

kripken commented 11 years ago

The checksums differ between machines, i am afraid. might be antialiasing or GL driver differences. so I am not that surprised to see differences. If the native build looks close to the web one, though, then I would assume it is fine.

AkhRani commented 11 years ago

I've made a test which reproduces the first problem I ran into. I've uploaded it to https://github.com/AkhRani/emscripten/tree/bananabread_bounding_box/tests/glbegin_quads.c

I am attaching an apitrace screnshot from the C version of sauerbraten, to show that it does call glBegin/End after glEnable(GL_TEXTURE_2D). I looked in the cube2/src directory of BananaBread, and there are multiple calls to glEnable(GL_TEXTURE_2D) which are not guarded by #ifndef EMSCRIPTEN, so I think my test case is valid.

sauerbraten_trace

kripken commented 11 years ago

Not sure what I'm missing, but compiling that natively and with emcc, I seem to get the same output (red and green stuff). Am I not seeing the problem?

AkhRani commented 11 years ago

I meant to mention that you have to compile with assertions enabled. When I compile with emcc -O2 for example, it runs and displays OK.

kripken commented 11 years ago

I compiled without any optimizations, which has assertions on, emcc file.cpp -o file.html. -O2 also looks fine. What command are you using to compile it?

AkhRani commented 11 years ago

My apologies. My emcc was out-of-date. The assert I was seeing was fixed 10 days ago with commit 62100521. I'll check tomorrow or the next day to see if BananaBread's bounding boxes work with the latest emcc. Thanks for taking the time to look at this.

AkhRani commented 11 years ago

OK, finally got a chance to re-test. Everything looks OK. In case anyone is interested in the results of the experiment, occlusion queries don't help on Chromium. I think this is because the glGetQueryObject call has to go to the GPU thread and back to the render thread. I might see if I can figure out a way to get the query results asynchronously, but this whole thing is going to take a back burner for now.

kripken commented 11 years ago

The cube 2 docs says that occlusion queries can be very significant on large maps. Did you test on something really big with lots of potential occlusion, and without PVS?

AkhRani commented 11 years ago

I tried it on the two towers map, with PVS enabled. Using a native build, I had found occlusion queries significantly reduced the number of glDrawElements / DrawArrays calls in that scenario. I don't remember the exact numbers, but the reduction was in the 30-40% range. So, not a huge map, and not one with lots of visibility "chokepoints", but I expected some improvement, since the per-call overhead is relatively high. However, the frame rate with occlusion queries enabled was much worse than the baseline (without occlusion queries). A quick JS profile with the chrome developer tools showed that javascript was spending about 50% of its time just calling glGetQueryObject.

As far as I can tell, occlusion queries aren't even on the radar for the WebGL committee, so this was always more out of curiosity than anything useful. I would be interested to see the results on Firefox, if anyone is interested in doing that experiment. Basically, you just have to add the GL Query API to the WebGL .idl files and implement them in the browser, then pass the calls through the the emscripten layer. Chrome already had hooks for queries in its GPU message pipes, so that part wasn't too bad, though there were a couple of gotchas. I don't know the situation on Firefox, but presumably you have more direct access to the OpenGL context there.

kripken commented 11 years ago

Interesting. Well, too bad it isn't a clear win.