• Stars
    star
    160
  • Rank 233,860 (Top 5 %)
  • Language
    JavaScript
  • License
    Other
  • Created about 4 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Checks your WebGL usage for common issues

WebGL Lint

WebGL lint is a script you can throw into your WebGL project to check for common WebGL errors.

  • Calls getError after every function and throws if there was an error.

  • Checks that no arguments to any functions are undefined.

    Pass 0 or false or null where you mean 0 or false or null.

  • Checks that no numbers or values in arrays of numbers are NaN.

  • Checks that all non-sampler uniforms are set. (see configuration below)

  • Checks that uniform matrices are not all zero.

  • Warns if you try to access an undefined uniform.

  • Checks for out of range access issues and will tell you which attribute/s are out of range

  • Checks that shaders compile. On failure prints the shader and tries to highlight errors.

  • Checks that programs link. On failure prints the attached shaders.

  • If there is a WebGL error it tries to provide more info about why

    • for framebuffer feedback it will tell you which textures assigned to which uniforms and which attachments

    • for other errors it will try print extra info where possible.

    • it lets you name webgl objects so those names can be shown in the error messages.

Example

Open the JavaScript console. You'll see the first example prints fewer errors and less info where as the second prints much more info.

Usage

<script src="https://greggman.github.io/webgl-lint/webgl-lint.js" crossorigin></script>

or

import 'https://greggman.github.io/webgl-lint/webgl-lint.js';

WebGL Lint throws a JavaScript exception when there is an issue so if you are using try/catch to catch errors you might need to print the exceptions inside your catch block. You can also turn on "pause on exception" on your JavaScript debugger.

Throwing seemed more a appropriate than just printing an error because if you get an error you should fix it! I tried the script out with all the three.js examples. It found 1 real bug and several half bugs. By half bugs I mean there were several examples that functioned but were actually passing NaN or null in the wrong places for a few frames or they were not setting uniforms that probably should have been set. Arguably it's better to fix those so that you can continue to use the helper to find real errors. In any case most of examples ran without error so you can do it too! πŸ˜‰

GMAN_debug_helper extension

WebGL Lint adds a special extension GMAN_debug_helper with these functions

  • tagObject(obj: WebGLObject, name: string): void - see naming below
  • untagObject(obj: WebGLObject): void - see naming below
  • getTagForObject(obj: WebGLObject): string - see naming below
  • setConfiguration(settings): void - see configuration below
  • disable(): void - turns off the checking
  • getAndResetRedundantCallInfo(): RedundantCallInfo - see below

Configuration

You don't need to configure anything to use in general but there are some settings for special needs.

  • maxDrawCalls (default: 1000)

    Turns off the checking after this many draw calls. Set to 0 to check forever.

  • failUnsetUniforms: (default: true)

    Checks that you set uniforms except for samplers and fails if you didn't. It's a common error to forget to set a uniform or to mis-spell the name of a uniform and therefore not set the real one. The common exception is samplers because uniforms default to 0 so not setting a sampler means use texture unit 0 so samplers are not checked.

    Of course maybe you're not initializing some uniforms on purpose so you can turn off this check. I'd recommend setting them so you get the benefit of this check finding errors.

    Note: uniform blocks are not checked directly. They are checked by WebGL itself in the sense that if you fail to provide uniform buffers for your uniform blocks you'll get an error but there is no easy way to check that you set them.

  • failUnsetSamplerUniforms: (default: false)

    See above why sampler uniforms are not checked by default. You can force them to be checked by this setting.

  • failZeroMatrixUniforms: (default: true)

    Checks that a uniform matrix is not all zeros. It's a common source of errors to forget to set a matrix to the identity and it seems uncommon to have an all zero matrix. If you have a reason a matrix needs to be all zeros you may want to turn this off.

  • failUnrenderableTextures: (default: true)

    Unrenderable textures are not an error in WebGL, they just don't render. WebGL itself usually print's a warning but it's usually fairly cryptic just telling you an unrenderable texture exists but not much else.

    Examples of unrenderable textures are non-power of 2 textures in WebGL1 with filtering set to need mips and wrap not set to CLAMP_TO_EDGE or in both WebGL and WebGL2 would be mips of different internal formats or the wrong size.

  • failUndefinedUniforms: (default: false)

    WebGL by default returns null when you call gl.getUniformLocation for a uniform that does not exist. It then silently ignores calling gl.uniformXXX if the location is null. This is great when you're editing a shader in that if you remove a uniform from the shader your code that is still setting the old uniform will keep working.

    For example if you are debugging and you go to the bottom of your fragment shader and add gl_FragColor = vec4(1, 0, 0, 1); all the uniforms in your fragment shader will be optimized out. If WebGL suddenly issues errors trying to set those it would be much more frustrating to debug. Conversely though, if you have a typo, for example you want to look up the location of 'u_color' and you type gl.getUniformLocation(prg, 'uColor') you'll get no error and it will likely take you a while to find your typo.

    So, by default webgl-lint only prints a warning for undefined uniforms. You can make throw by setting failUndefinedUniforms to true.

  • failBadShadersAndPrograms: (default: true)

    Most WebGL programs expect all shaders to compile and all programs to link but often programmers don't check for errors. While it's likely they'd get an error about a bad program further in their code, at that point it's likely too late to tell them it's because the program didn't compile or link. Instead the message will just be something like "no valid program in use".

    If you're working on a project that expects shaders to fail to compile and/or programs to link you can set this to false.

  • warnUndefinedUniforms: (default: true)

    See failUndefinedUniforms. Setting this to false turns off warnings about undefined uniforms.

  • ignoreUniforms: (default: [])

    Lets you configure certain uniforms not to be checked. This way you can turn off checking for certain uniforms if they don't obey the rules above and still keep the rules on for other uniforms. This configuration is additive. In other words

    ext.setConfiguration({ignoreUniforms: ['foo', 'bar']});
    ext.setConfiguration({ignoreUniforms: ['baz']});

    Ignores uniforms called 'foo', 'bar', and 'baz'.

  • throwOnError: (default: true)

    The default is to throw an exception on error. This has several benefits.

    1. It encourages you to fix the bug.

    2. You'll get a stack trace which you can drill down to find the bug.

    3. If you use "pause on exception" in your browser's dev tools you'll get a live stack trace where you can explore all the local variables and state of your program.

    But, there might be times when you can't avoid the error, say you're running a 3rd party library that gets errors. You should go politely ask them to fix the bug or better, fix it yourself and send them a pull request. In any case, if you just want it to print an error instead of throw then you can set throwOnError to false.

  • makeDefaultTags: (default: true)

    If true, all objects get a default tag, Example *UNTAGGED:Buffer1, *UNTAGGED:Buffer2 etc. This is a minor convenience to have something to distinguish one object from another though it's highly recommended you tag your objects. (See naming).

    The only reason to turn this off is if you're creating and deleting lots of objects and you want to make sure tags are not leaking memory since tags are never deleted automatically. (See "naming).

There 2 ways to configure

  1. Via the extension and JavaScript.

    Example:

    const gl = someCanvas.getContext('webgl');
    const ext = gl.getExtension('GMAN_debug_helper');
    if (ext) {
      ext.setConfiguration({
        maxDrawCalls: 2000,
        failUnsetSamplerUniforms: true,
      });
    }
  2. Via an HTML dataset attribute

    Example:

    <script
      src="https://greggman.github.io/webgl-lint/webgl-lint.js"
      data-gman-debug-helper='
        {
          "maxDrawCalls": 2000, 
          "failUnsetSamplerUniforms": true
        }
      '>
    </script>

    Note: (1) the setting string must be valid JSON. (2) any tag will do, <div>, <span>, etc. as the script just applies all tags it finds with querySelectorAll('[data-gman-debug-helper]') and applies the options in the order found.

Naming your WebGL objects (buffers, textures, programs, etc..)

Using the extension you can name your objects. This way when an error is printed the names will be inserted where appropriate.

const ext = gl.getExtension('GMAN_debug_helper');
const tex = gl.createTexture();
ext.tagObject(tex, 'background-tex');

Now if you get an error related to tex you might get an told it's related to 'background-tex' instead of just that you got an error.

4 suggestions for using naming

  1. make some helpers

    const ext = gl.getExtension('GMAN_debug_helper');
    const tagObject = ext ? ext.tagObject.bind(ext) : () => ();

    now you can just unconditionally tag things and if the extension does not exist it will just be a no-op.

    const tex = gl.createTexture();
    tagObject(tex, 'checkerboard');
  2. wrap the creations functions

    const ext = gl.getExtension('GMAN_debug_helper');
    if (ext) {
      Object.keys(gl.__proto__)
        .filter(name => name.startsWith('create'))
        .forEach(name => {
          const origFn = gl[name];
          if (origFn) {
            gl[name] = function(...args) {
              const obj = origFn.call(this, ...args);
              if (obj) {
                ext.tagObject(obj, args[args.length - 1] || '*unknown*');
              }
              return obj;
            }
          }
        });
    }

    Which you use like this

    const shader = gl.createShader(gl.VERTEX_SHADER, 'phongVertexShader');
    const tex = gl.createTexture('tree-texture');

    and they'll still work in normal WebGL as it will ignore the extra parameter.

  3. Same as above but not wrapped

    const ext = gl.getExtension('GMAN_debug_helper');
    const api = Object.fromEntries(
        Object.keys(gl.__proto__)
          .filter(name => name.startsWith('create'))
          .map(name => {
            const func = (ext && gl[name])
              ? function(...args) {
                  const obj = gl[name](...args);
                  if (obj) {
                    ext.tagObject(obj, args[args.length - 1] || '*unknown*');
                  }
                  return obj;
                }
              : function(...args) {
                  return gl[name](...args);
                };
            return [name, func];
          }));

    Which you use like this

    const shader = api.createShader(gl.VERTEX_SHADER, 'phongVertexShader');
    const tex = api.createTexture('tree-texture');

    If you're allergic to hacking native APIs this is better but you have to remember to use api.createXXX instead of gl.createXXX

  4. Use your own API.

    Lots of people have wrapped WebGL themselves with things like class Texture and class Framebuffer or other functions. Those would be a good place to integrate tagging.

As a simple example, naming buffers after the attributes they'll be used with (eg. 'position', 'normal'), naming textures by the URL of the img where they get their data. Naming vertex array objects by the model ('tree', 'car', 'house'), naming framebuffers by their usage ('shadow-depth', 'post-processing'), naming programs by what they do ('phong-shading', 'sky-box')...

You can also untag an object with

ext.untagObject(someObj);

Arguably for debugging you probably don't want to untag. The problem with untagging is if you untag and then have a bug where you reuse an untagged object, for example using an object you deleted, there will be no tag for the object, so your error will just say something like "error: tried to use Texture(unknown)" instead of "error: tried to use Texture(yourTag)". At the same time, if you're running WebGL-lint indefinitely (see maxDrawCalls) and creating and deleting lots of objects, for example sync objects, there is a tiny bit of memory involved keeping the label of each one so manually untagging should mean you are not leaking memory. Honestly I wouldn't worry about untagging but it's here just in case you've got special needs. Also see makeDefaultTags above.

The extension also includes getTagForObject if you want to look up what string you tagged an object with

const buf = gl.createBuffer();
ext.tagObject(buf, 'normals');
console.log(ext.getTagForObject(buf));  // prints 'normals'

Checking for redundant calls

An example of a redundant call is calling gl.useProgram with the same program or calling gl.vertexAttribPointer with the same parameters and the same buffer. You can get a count to date of the redundant state setting WebGL-Lint has detected by calling ext.getAndResetRedundantCallInfo()

Example:

function render() {
  // .. do stuff with webgl ..

  const info = ext.getAndResetRedundantCallInfo();
  console.log(JSON.stringify(info));
  requestAnimationFrame(render);
}

Alternatively you can try adding this script to your page (instead of webgl-lint.js) and it will attempt to print redundant call info for you.

<script type="module" src="https://greggman.github.io/webgl-lint/webgl-lint-check-redundant-state-setting.js"></script>

or

import 'https://greggman.github.io/webgl-lint/webgl-lint-check-redundant-state-setting.js';

Note: WebGL-Lint does not check every possible redundant set setting. At the moment it checks

  • bindBuffer
  • bindFramebuffer
  • bindRenderbuffer
  • bindSampler
  • bindTexture
  • bindVertexArray
  • enable
  • disable
  • vertexAttribPointer
  • vertexAttribIPointer

It's not important to avoid 100% of redundant state setting. Rather, you can use this feature to see if you have too much redundant state setting. For example if you're making 2000 draw calls and you see 500-5000 redundant state setting counts then you should probably look into adding some state tracking in your own code or organizing the way calls happen so state is not set. It's not so much that setting state more than once is bad, it's rather than it's just proof your code is doing extra work. If it's a lot of extra work then you probably want to look into it. If it's only a small amount of extra work then don't worry about it.

Suggestions?

https://github.com/greggman/webgl-lint/issues

Development

You can run the tests with the un-merged code with http://localhost:8080/test/?src=true. You can also filter the tests with grep= as in http://localhost:8080/test/?grep=shader or both http://localhost:8080/test/?src=true&grep=shader.

Repo

https://github.com/greggman/webgl-lint/

License

MIT

More Repositories

1

twgl.js

A Tiny WebGL helper Library
JavaScript
2,451
star
2

better-unity-webgl-template

A better default template for Unity WebGL
HTML
630
star
3

HappyFunTimes

A System for creating 10-100+ player local games
JavaScript
371
star
4

html5bytebeat

Bytebeats in HTML5
JavaScript
369
star
5

webgl-memory

A library to track webgl-memory
JavaScript
306
star
6

tdl

A low-level WebGL library
JavaScript
280
star
7

ffmpegserver.js

Receives canvas frames from browser to generate video on the server. Compatible with CCapture.js
JavaScript
267
star
8

servez

A simple web server for local web development.
JavaScript
258
star
9

hsva-unity

A Hue Saturation Value adjustment shader for Unity. Useful for making lots of character colors.
GLSL
202
star
10

wgpu-matrix

Fast WebGPU 3d math library
JavaScript
129
star
11

virtual-webgl

Virtualize WebGL Contexts
JavaScript
105
star
12

unzipit

Random access unzip library for JavaScript
JavaScript
100
star
13

unity-webgl-copy-and-paste

Support Copy and Paste in Unity WebGL
C#
90
star
14

doodles

Random JavaScript doodles
JavaScript
58
star
15

getuserimage-unity-webgl

How to ask the user for a photo in Unity-WebGL
C#
55
star
16

webgl-helpers

some tiny webgl scripts that might come in handy
JavaScript
54
star
17

webgpu-memory

Track your WebGPU memory usage
JavaScript
43
star
18

servez-cli

The cli version of servez
JavaScript
38
star
19

ImHUI

Experimental UI
TypeScript
34
star
20

webgl-capture

code to help make a reduced test case for WebGL by capturing the commands and generating a stand alone program
JavaScript
34
star
21

oes-vertex-array-object-polyfill

WebGL OES_vertex_array_object polyfill for GPUs/Drivers/Browsers that don't have it
JavaScript
33
star
22

hft-unity3d

Unity3D Libraries for HappyFunTimes
C#
31
star
23

requestanimationframe-fix.js

Fix for requestAnimationFrame with lots of elements
JavaScript
31
star
24

webgpu-utils

Some helpers for webgpu
JavaScript
30
star
25

youtube_chromecast_speed_hack

A way to play a youtube video on Chromecast with settable speed
HTML
28
star
26

pico-8-post-processing

post process pico-8
JavaScript
28
star
27

hft-tonde-iko

A Multi-Machine Platformer
JavaScript
24
star
28

react-split-it

A React Based Splitter
JavaScript
24
star
29

pixel-perfect.js

Display Image Pixel Perfect
HTML
22
star
30

gradient-editor

A Jquery based gradient editor
JavaScript
20
star
31

jsgist

A code playground that stores data as github gists
JavaScript
16
star
32

interval-timer

A Simple Interval Timer
JavaScript
16
star
33

html5-gamepad-test

HTML
15
star
34

webgpu-avoid-redundant-state-setting

Check for and avoid redundant state setting
JavaScript
15
star
35

webgl-canvas-2d

A minimal implementation of the canvas 2D API through WebGL
JavaScript
14
star
36

rockfall

Rockfall. A game where rocks fall
JavaScript
14
star
37

jsbenchit

A JavaScript benchmark static page website that uses gists to store benchmarks
JavaScript
14
star
38

fanfictionreader

Reads your fanfiction to you.
JavaScript
13
star
39

happyfuntimes.net

The HappyFunTimes.net code
JavaScript
10
star
40

dekapng

Make giant PNG files in the browser
TypeScript
10
star
41

hft-gamepad-api

Emulates the HTML5 Gamepad API using smartphones and HappyFunTimes
JavaScript
10
star
42

hft-boomboom

A happyfuntimes game with splosions
JavaScript
9
star
43

DeJson.NET

A simple serialization library from JSON to C# classes based on MiniJSON good for Unity3D
C#
9
star
44

oculus-anti-spy

Try top stop Facebook from spying on all Oculus Activity
JavaScript
8
star
45

uzip-module

An ES6 module version of UZIP.js
JavaScript
8
star
46

imageutils

A few image utils for in browser JavaScript
JavaScript
7
star
47

hft-unity-gamepad

A Generic HappyFunTimes Gamepad for Unity
JavaScript
7
star
48

MoPho-V

A Community Supported Movie and Photo Viewer
JavaScript
6
star
49

sharks-with-frickin-lasers

JavaScript
6
star
50

hft-clean

The simplest happyfuntimes example, no other scripts
JavaScript
6
star
51

webgpu-helpers

Small scripts useful when debugging or developing webgpu
JavaScript
6
star
52

octopus

JavaScript
5
star
53

audiostreamsource.js

Provides a streamed audio source for WebAudio across browsers
JavaScript
5
star
54

dump-all-the-shaders

A script you can add to dump all your shaders to the console.
JavaScript
4
star
55

epub-viewer

A simple epub viewer. Client side only
HTML
4
star
56

image-grid

A simple image-grid for displaying images um, in a grid in the browser
JavaScript
4
star
57

simple-new-tab-page

A simple new tab page extension
JavaScript
4
star
58

other-window-ipc

IPC between windows in Electron
JavaScript
4
star
59

macos-opengl-experiments

Simple OpenGL stuff on MacOS
Objective-C++
3
star
60

rest-url

Makes REST urls
JavaScript
3
star
61

aws-oauth-helper

An AWS Lambda function to handle the oauth client secret part of oauth
JavaScript
3
star
62

unity-load-mp3-at-runtime

example of loading mp3 at runtime
C#
3
star
63

soundcloud-audio-reactive-example

Soundcloud audio reactive example using new API
JavaScript
3
star
64

hft-unityvideofromunity

An example of sending WebCam video FROM unity to the controller (phones)
C#
3
star
65

muigui

baking
JavaScript
3
star
66

hft-syncthreejs

Shows syncing a three.js example across multiple machines using HappyFunTimes
JavaScript
3
star
67

fixallthetags

SO script to fix tags
JavaScript
3
star
68

hft-local

Run a HappyFunTimes game without HappyFunTimes (no networking .. sometimes good for demos)
JavaScript
3
star
69

screenshot-ftw

screenshot a window across OSes
C++
3
star
70

webgl-benchmarks

WebGL Benchmarks (NOT GPU BENCHMARKS!!!)
JavaScript
3
star
71

opengl-fundamentals

JavaScript
3
star
72

stackoverflow-getallanswers

Get all answers for a particular user (and all the questions for those answers)
Python
3
star
73

hft-unitysimple

The simplest Unity example for HappyFunTimes using C#
C#
3
star
74

jsgistrunner

JavaScript
2
star
75

fisheye-skybox-unity

Make a fisheye skybox shader in unity
ShaderLab
2
star
76

cssparse.net

CSS string to Unity3D Color parser
C#
2
star
77

u2b-ux

better youtube ux
JavaScript
2
star
78

hft-unity-character-select

A HappyFunTimes Unity example showing spawning different prefabs based on player character selection
C#
2
star
79

native-msg-box

Allows you to display a native MessageBox / Dialog from node.js
JavaScript
2
star
80

hft-simple

A simple example for HappyFunTimes
JavaScript
2
star
81

hft-jumpjump

The HappyFunTimes JumpJump Example Platformer
JavaScript
2
star
82

servez-lib

The server part of servez
JavaScript
2
star
83

check-all-the-errors

load all your pages, check for javascript errors
JavaScript
2
star
84

hft-unity-2-button-gamejam

A Unity HappyFunTimes Template for the Pico Pico Cafe 2 Button Gamejam
C#
2
star
85

hft-simple-no-electron

an example of using happyfuntimes without electron
JavaScript
2
star
86

eslint-plugin-one-variable-per-var

Enforce one variable declaration per var statement
JavaScript
2
star
87

ldcp

low dependencies cp for node
JavaScript
2
star
88

LUT-to-PNG

Convert a LUT or CUBE file to a PNG (for Unreal / Unity)
JavaScript
2
star
89

hft-powpow

A simple space shooter game for HappyFunTimes
JavaScript
1
star
90

hft-utils

Various JavaScript files shared among HappyFunTimes example games
JavaScript
1
star
91

vertexshaderart.org

vertexshaderart.org
1
star
92

hft-sync2d

Example showing syncing canvas 2d across machines using HappyFunTimes
JavaScript
1
star
93

dns-server

Automatically exported from code.google.com/p/dns-server
C++
1
star
94

bloom

look at the source
JavaScript
1
star
95

hft-unity-cardboard

Example of using HappyFunTimes with Unity and Google Cardbard
C#
1
star
96

webgpu-dev-extension

Explorational WebGPU Dev Extension
JavaScript
1
star
97

font-utils

Some font utils I wrote to generate fonts for gamemaker
C
1
star
98

jsbenchit-comments

just a point to host jsbenchit comments on another domain for security
HTML
1
star
99

hft-c

HappyFunTimes support for C / C++ based games
C++
1
star
100

hft-exe

HappyFunTimes executable creator
C
1
star