Friday 21 October 2011

Why Developing for WebGL Sucks!



For some time now I have been working with WebGL and have developed a sort of love/hate relationship with it. I love the ability to instantly target millions of people with GPU accelerated code without any plugins or barriers (excluding the targets that dont support it). However as a developer, writing code that takes advantage of WebGL kinda sucks.

Procedural Based


First off is the way you have to structure your GL calls. For example take a look at the following generic bit of webGL harvested from the net:

[codesyntax lang="javascript" lines="normal"]
texture = gl.createTexture();
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, 64, 64, 0,
gl.RGB, gl.FLOAT, new Float32Array(pix));
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

texture1 = gl.createTexture();
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, texture1);
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, 64, 64, 0,
gl.RGB, gl.FLOAT, new Float32Array(pix1));
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

FBO = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, FBO);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D, texture, 0);
FBO1 = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, FBO1);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D, texture1, 0);
if( gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE)
alert(err + "FLOAT as the color attachment to an FBO");

[/codesyntax]

All it does is create a couple of textures, setting their starting values and creates two frame buffers for rendering to. Its just it looks complicated and difficult to understand.

GL works on a procedural basis, so you tell GL that you are about to work on something by calling a function like "bindTexture()" then on the next line you perform an operation on it such as "pixelStorei()". Now this may have made perfect sense back when we were writing everything in C which is procedural anyway however this is Javascript (or haXe in my case) which is an Object based language, code like this is difficult to understand and follow.

The procedural nature of WebGL means you have to be much more careful about unsetting things you have previously set. For example if you bind a texture to perform some operation, you must then remember to unbind it else you could inadvertently cause operations to be applied to it on subsequent calls elsewhere in your codebase. It this 'hidden state' that has caused me alot of headaches when developing my samples.

The principal behind WebGL was to provide a very low-level library which other developers can build upon to build more complex and abstracted libraries. And there are numerous libraries out there. I personally have tried several of them including the very popular three.js. Three.js is great for doing common things like loading models and putting quads on the screen. I however encountered a problem with render targets which I struggled with for days before I discovered that you had to set "needsUpdate" to true on your texture before using it. In the end I decided to drop three.js beacuse of another issue I encountered and instead attempt to reduce my complications by working with webGL directly.

Flash11's Stage3D has the same philosophy as webGL, to provide a low level API for other developers to build libraries upon. The thing is Flash11's low-level API makes more sense and is more readable. For example the following to me is much more readable than its webGL equivalent:

[codesyntax lang="actionscript3" lines="normal"]
texture = c.createTexture(logo.width, logo.height, Context3DTextureFormat.BGRA, false);
texture.uploadFromBitmapData(logo);

[/codesyntax]

The Stage3D API also uses language like "upload" to let you know when you are transferring data to the GPU, for a new comer to GL you have no clue when things are going to the GPU. Its small things like this that reduce the "WTF?" factor when tackling the tricky world of hardware-accelerated 3D programming.

Cross-domain textures


This one cropped up around July time this year and took me ages to work out what was going on. For some inexplicable reason (or so it seemed) my code one day stopped working. When I looked for demo code online it all worked fine, however when I downloaded it and run it locally it also didnt work. I was getting errors like the following:

Uncaught Error: SECURITY_ERR: DOM Exception 18
Uncaught Error: SECURITY_ERR: DOM Exception 18
Uncaught Error: SECURITY_ERR: DOM Exception 18
Uncaught Error: SECURITY_ERR: DOM Exception 18
Uncaught Error: SECURITY_ERR: DOM Exception 18 

I was so baffled that I posted about it on the HaXe mailing list asking for help, thinking it was something I was doing wrong with HaXe. It turns out (after much wall-head-butting) this was a change that they brought into Chrome 13 and Firefox 5 to combat a security problem when using shaders that use textures from a different domain to the one running the code:

http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html

Now I have no problem with cross-domain issues, im used to this from Flash where we have the same sort of setPixel() restrictions on cross-domain BitmapData's. The thing is, it appears that this restriction applies when running code locally too. So If you are developing something on your local machine and trying to access a texture from disk it throws the same security errors because the browser thinks you are reaching across domains to access the image.

At the time the only way to get around this was to create your own webserver that you run on localhost to server up the files. So to do that I had to download python so I could run a simple localhost commandline webserver from my bin directory. What an effort! There may be easier ways these days to solve it but at the time it really frustrated me and formed yet another barrier to developing webGL.

No Error Messages


This is by far the most annoying thing about developing for WebGL. So many times I have been trying to write something that I know SHOULD work but for some reason it doesn't. I dont get any error messages, nothing. It makes writing something from scratch neigh on impossible.

In my last post "GPU State Preserving Particle Systems with WebGL & HaXe" I started with an idea. I attempted to code it 'bottom-up'. That is start with nothing and then add more code until I reached what I wanted. Unfortunately having no error messages in WebGL makes this very difficult indeed. I would spend some time writing something really simple, like trying to get a textured quad to render on the screen only to find I get nothing. I double check my camera matrices my vertex and texture buffers, my shader and still nothing. Eventually I found that I hadn't bound something first before trying to operate on it *sigh*

In the end I found the best way to get anywhere is to go from the other direction, a 'top-down' method. Start with something kind of simmilar to what you want then cut bits out one line at a time until you get what you want. Its extremely time consuming and frustrating, but its less frustrating than going from the other way.



There are tools out there that help with debugging what is going wrong. Namely the WebGL Inspector (see above) is intended to provide gDEBugger / PIX like debugging information about what is going on inside webGL. Its a clever bit of tech, it lets you inspect buffers and traces each gl call, however it suffers from the same underlying problem of having no errors. You setup a buffer incorrectly and what you get is "INVALID_VALUE". No indication as to which of the values is invalid or what part of the call you messed up on :(

Googling Doesn't Help


If you do happen to get an error message (unlikely) or you word your problem in a sufficiently succinct and googaleble way you will then run into the next big problem with WebGL; theres very few people using it. Now I know I am likely to be flamed for that comment, but it just seems that way to me. Whenever I tried to google my problem, or google for what I was trying to achieve (because working bottom-up doesnt work) there would be a very sparse smattering of relevant posts. Usually the results are forum posts and are OpenGL not WebGL related and are from 5-10 years ago.

But..


Now having just ranted on for several hundred words about why it sucks im going to finish it off by saying that im going to continue to develop for WebGL using haXe regardless. Why? Well I just like making pretty things that run fast and GPGPU programming appeals to me for some unknown (likely sadistic) reason.

14 comments:

  1. Re: 'No error messages': in Firefox if you go to about:config and set webgl.verbose=true, you get JS warnings for every WebGL error and more. This is very discoverable too: if you don't have  webgl.verbose=true you'll get just 1 message suggesting that you set it.

    Re: 'Cross-domain textures': both Firefox and Chrome allow you to loosen security rules for local files, just make sure to only do this for testing and not for real browsing as it's really insecure. In Firefox, set security.fileuri.strict_origin_policy=false.

    Re: 'Procedural based': the intent has always been that if people want nice APIs, they can implement them on top of WebGL. There are plenty of nice object oriented frameworks around WebGL, like three.js.

    ReplyDelete
  2. Hi Benoit,

    Cheers for your comments.

    Yes I have recently become aware of the webgl.verbose option in firefox however when I tried it out (by purposely setting some bad values in GL calls) I got no errors :( Also another problem is that the WebGL inspector doesnt currently support Firefox so that definitely limits my ability to do productive development in the browser.

    Im glad they improved the error message you get in the browser for cross-domain textures now. When it first started happening to me there was no error in Chrome or Firefox, it caused me many days worth of problems. 

    Yes as I mentioned in my post WebGL AND Flash11 were designed so that more stuff could be built ontop of it and im definitely behind this philosophy. However take a look at the Flash11 API compared to the WebGL one and tell me that it isnt easier to understand. All im saying is that rewording the calls, simplifying some stuff, making it less procedural would have gone a long way to making it easyier to use. hats just my opinion. 

    As for using a third party API such as Three.js; im all for that, but personally I like to try to understand whats going on down low first. As mentioned in my post there are difficulties with third party libraries too related to the user not knowing that he has to set certain properties for functionality to work correctly (see my comment on the 'needsUpdate' property).

    ReplyDelete
  3. It seems that you've been looking at WebGL errors at the wrong place, because I guarantee that they've been around since before Firefox 4 was released. Maybe the one tricky thing is that WebGL errors are reported as JS _warnings_ in Firefox, because that best describes their level of severity. In Firefox, in the Web console (Ctrl+Shift+K), enable the JS tab and make sure warnings are enabled. The old JS Error Console (Ctrl+Shift+J) has them too.

    Again, making WebGL be a straightforward direct mapping to the OpenGL API was a conscious design decision. This way, people can implement whatever better API they want on top of WebGL, and they do: there are dozens of WebGL frameworks already, some of which are very good (three.js, CubicVR.js, GLGE...).

    I fully understand the desire to use WebGL directly and see what's really going on, but that means understanding what's going on at the level of OpenGL, which is facilitated by WebGL staying very close to OpenGL. (The alternative, of course, would have been to stay close to Direct3D).

    ReplyDelete
  4. Ah I didnt know about the FF JS console. I had been using the Firebug console and assumed that it would be picking up any errors that WebGL may have been throwing. If this should be the case then im not sure what was going wrong then.

    Next time I do some webGL ill try the FF console out. Cheers

    ReplyDelete
  5. Google chrome is more robust when it comes to WebGL and Javascript, also include built-in JS debugger and (optional) webgl debugger extension, also you can use *console.log()* and *console.error()* commands to write a complete debugger for your engine, which is a hug time saver.

    With that being said, I don't like WebGL since I don't like the idea that my life depends on "a web browser", working with OpenGL and C/C++ is much more enjoyable than JS and WebGL in my opinion.
    You can load assets (model, texture, shader) from files, you have full-access to keyboard, mouse and joysticks, control-flow supported by glsl shaders, you can compile your code for commercial projects unlike JS, and OpenGL is defacto standard in the world of 3D-CG for a decade and half.

    I think WebGL is fun for experimenting, but this technology is half-baked and not usable at this time, maybe 2020, maybe never, like VRML and more recently Flash?!

    Sorry for language, just trying to help
    Regards

    ReplyDelete
  6. Agree, tried to test drive it (be kind, am new to JS and Jasmine):
    http://lampkin.apphb.com/Test/WebGl

    It was a chore, too much going on with the "gl" object. You can see from the amount of mocking I'm having to do, far too much going on around each behaviour.

    What I'm finding worse is due to it's unintuitive nature I'm seeing similar frustrating coding practises cropping up in all WebGl examples. The "gl" god object combined with duck-typing being the cause of this where a lot of examples effecively use it as a global store. Often I search through the API wondering what certain properties mean only to find someone's just added them on.

    Still, as you say, it's strangely addictive.

    ReplyDelete
  7. Very much agreed seb, I too encountered alot of those samples where people were using the gl object as a global store, extremely confusing for a novice to the platform to follow. Nice work on the Jasmine tests ;)

    ReplyDelete
  8. if you don't like coding for webgl try creating something for webgl with maya and www.inka3d.com ;-)

    ReplyDelete
  9. Interesting.. Not heard of that before, thanks for sharing!

    ReplyDelete
  10. Quit with your hate, WebGL easier to develop for than any desktop implementation. In Chrome, just hit Ctrl+Shift+J for a great debugging experience. All of your claims are absolute bullshit.

    ReplyDelete
  11. Quit being a nag. WebGL is a baby and it could someday redesign the web. It's powerful and understandable by any respectable programmer. As far as documentation and community, like I said, it's still a new technology and as support increases those will grow too. I can't wait too see the day where you wont need to be the bitch of JS plugins, jQuery, CSS, etc, etc, just to accomplish a simple silly slider. You can make something much nicer with less coding with WebGL alone, using real coding (no markup), if you know what you are doing!

    PS: I do love jQuery tho.

    ReplyDelete
  12. I wholeheartedly agree, it could be easier to program. I want to see a library with a dead simple, intuitive interface which only exposes features as the programmer needs them. The 2d canvas has libraries that are getting close. Something that fills the gap between wysiwyg 3d design packages and the api libraries that exist currently, so programmers can get right onto the task.

    ReplyDelete
  13. You're right about that GL object being unwieldy. I feel the same way about Flash's Context3D. Why should I be handing an entire GL context to a mesh object, if it only needs it to create some buffers, or to initialize a texture?

    For my Haxe project, I'll be slicing up GL contexts' interfaces into smaller objects that expose specific functions to the objects that use them. It'll probably help maintain cross-platform compatibility as well.

    ReplyDelete
  14. Aye well I understand the "low level" thing, thats fine but IMO flash's "low-level" API just makes more sense

    ReplyDelete