• Stars
    star
    105
  • Rank 328,196 (Top 7 %)
  • Language
    JavaScript
  • Created over 6 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

Virtualize WebGL Contexts

Virtual WebGL

Virtualizes a single WebGL context into multiple contexts

A demo of some WebGL apps running with only 1 shared WebGL context and using alpha: false, premultipliedAlpha: false, preserveDrawingBuffer: true and some other things. A similar demo for WebGL2. And, one for a mix of WebGL1 and WebGL2.

A demo of creating and disposing of webgl contexts. WebGL implementations often delete the oldest context when too many are created. Using virtual-webgl you can solve this issue. Note you are still responsible for freeing all WebGL resources used by the context. All buffers, textures, renderbuffers, framebuffers, shaders, and programs. On top of that when you're done with the context call context.dispose() which gives virtual-webgl a chance to free its internal stuff as well.

A demo of post processing a Unity app from outside Unity. Compare to the original without post processing.

A demo displaying mapbox-gl results on a cube.

What?

Browsers usually limit WebGL to 8 to 16 contexts max. This is one idea to overcome that limit.

If you're in control of your code then there are arguably better solutions like this for raw webgl and this for three.js (both if which I wrote BTW 😛)

I mostly wrote this for a fun short technical challenge. I have no plans to actually use it or maintain it. If you find a problem feel free to file an issue but I can't promise I can spend anytime addressing it. Pull requests are more welcome or just fork it.

Some things you could do with this other than just get more contexts:

Share resources across contexts.

Since underneath there is really only one context from the POV of the app you can do things like

const gl1 = document.createElement("canvas").getContext("webgl");
const gl2 = document.createElement("canvas").getContext("webgl");

const tex1 = gl1.createTexture();

gl2.bindTexture(gl1.TEXTURE_2D, tex1);  // this would normally fail

With normal WebGL contexts you can't use resources from one context in another context but with virtual contexts you can.

This is actually probably the best use-case. You can write 2 different libraries independently of each other using WebGL and have them share resources and they won't have to worry about stepping on each other's WebGL state. An example might be, you have a video conferencing library and you want to add an effects library.

Use the output of one WebGL app inside another

For example use a mapbox-gl-js map inside a three.js app.

I suppose you can already do this by just copying the canvas of the first app into the second but that copy is probably slow where as just rendering to a texture is fast.

See the mapbox-gl demo. It creates it's own compositor.

How to use?

Include it on your page before other scripts

<script src="virtual-webgl.js"></script>

or for WebGL2 use

<script src="virtual-webgl2.js"></script>

Writing your own compositor

The compositor is the part of virtual-webgl that's responsible for updating the individual canvas. The default compositor draws the framebuffer representing the drawingbuffer of that canvas, into an offscreen canvas and then calls canvas 2D's drawImage to get the WebGL results into the canvas. You can change this to do something else by providing your own compositor.

A full solution would probably require some other method but ... If you look in unity-example/index2.html you'll see code that creates a custom compositor that draws a different result than the default compositor.

The idea for the createCompositor function is that you probably need different compositors for each canvas on the page so it's up to you how to do that. For example you could check the canvas passed in and its ID or some data attribute and do create different compositors for different canvases. If you return nothing/undefined the default compositor will be created for that canvas.

As another example, if you wanted to draw a MapGL texture inside THREE.js then you'd probably make the one compositor do nothing except record the texture needed to use inside three.js. For three's canvas you'd use the default compositor. see this. I was not sure how to use an external WebGL texture in THREE.js so the example uses twgl.

Note: If a compositor has a dispose method it will be called if context.dispose is called to give your custom compositor a chance to clean up.

Limits and Issues

  • In WebGL2 you must end queries and transformFeedback before exiting the current event. The good things is, AFAIK, pretty much all WebGL2 apps that use queries and/or transformFeedback already do this so it should't be a problem. But, not finishing those before exiting the event is not technically against the spec.

  • WebGL1 is emulated on WebGL2 in virtual-webgl2.js

    When using virtual-webgl2.js WebGL2 functions are not available on virtual WebGL1 contexts but WebGL2 constants can be passed to WebGL1 contexts.

    In other words:

    webgl1Ctx.texImage3D(...);                         // error! no such function
    webgl1Ctx.bindTexture(webgl1ctx.TEXTURE_3D, tex);  // error! TEXTURE_3D is not defined
    webgl1Ctx.bindTexture(webgl2ctx.TEXTURE_3D, tex);  // ok

    This should arguably not a come up. A WebGL1 context would be using WebGL1 constants. I only point this out to say virtual-webgl doesn't force WebGL1 compliance.

  • WebGL1 on WebGL2 support is limited

    I took a quick stab at trying to emulate WebGL1 on WebGL2 so that you could mix WebGL1 and WebGL2 in the same WebGL2 context. That includes extensions like OES_vertex_array_object, OES_texture_float, and ANGLE_instanced_arrays.

    Unfortunately, when I got to WEBGL_draw_buffers I realized that emulating that would require a full GLSL parser and re-writer to change GLSL ES 1.0 to GLSL ES 3.0 and several complex transformations.

    That's the long way of saying WebGL1 emulation on WebGL2 is incomplete.

  • There are no checks for errors.

    WebGL (and OpenGL) use a asynchronous command buffer error system which means checking for errors really slows things down so this Virtual WebGL also doesn't check for errors. Your code should not be generating errors in the first place so if it is fix your code!

    Where this might come up? I forget the details of the spec but, lets say you make an invalid program. When you call gl.useProgram it will fail and leave the previous program still the current program. so if you call gl.uniformXXX you'll be setting the uniforms for the previous current program. With one WebGL app that was your own previous current program. With multiple WebGL apps that could be some other app's current program.

    That's just one example. Lots of apps don't setup textures correctly at the beginning, start loading images, start rendering, get errors, those errors stop when the image finally downloads. Those kinds of errors could bleed from one context to another.

    So, run your apps without virtual-webgl and make sure they get no errors. Then after try running them with virtual-webgl and they should hopefully still get no errors.

Perf

The WebGL2 wrapper (virtual-webgl2.js) is newer and saves state so it's pretty fast. The WebGL1 wrapper (virtual-webgl.js) is older and queries state so it's pretty slow.

You could probably get even more perf by changing all the functions that set GL state to just save the state but not actually set it and then just set the dirty state at draw time. And further, for all the functions that query state just return the saved state instead of asking WebGL. That would actually probably a speedup for many poorly written apps that query things they shouldn't.

Another perf issue is you can't render directly to different canvases so I have to make each of the canvases use a Canvas2DRenderingContext and call drawImage. That could be solved maybe with OffscreenCanvas and ImageBitmapRenderingContext but those features haven't shipped without a flag as of 2018-06-05.

It could also be solved using the techniques used in this sample

Basically, put the canvas of the shared GL context full window size in the background and instead of compositing by copying to a 2D canvas, composite by setting the viewport/scissor and render to the shared GL context's canvas. The limitation of course is that the result won't appear in front of other elements but usually that's ok.

That should be trivial to implement using a custom compositor. The first time you get a compositor put the canvas of the shared context (the one that gets passed to composite) in the page and then render the texture being composited using gl.viewport and gl.scissor

If your canvases are not all on screen you could try using an augmented requestAnimationFrame that only calls the requestAnimationFrame callback to draw the canvases that are on screen.

Future Enhancements

virtual-webgl adds a dispose method to the virtual contexts letting you free the virtual context. As it is it leaves it up to the app to free all of its own GPU resources. dispose only disposes of internal resources.

It probably would not be that hard to track resources by context and free them on dispose. It's not 100% clear that's the right thing to do always. For example since virtual-webgl lets you share resources across contexts it would be a perfectly valid use-case to create a temporary context just to create some resources and the dispose of that context but keep the created resources around.

It's perfectly reasonable to do this yourself 100% outside virtual-webgl. You just either augment the context.

Example

const buffers = new Set();
gl.createBuffer = function(oldFn) {
  return function() {
    const b = oldFn.call(this);
    buffers.set(b);
    return b;
  }
}(gl.createBuffer);
gl.deleteBuffer = function(oldFn) {
  return function(b) {
    buffers.delete(b);
    oldFn.call(this, b);
  }
}(gl.deleteBuffer);
gl.dispose = function(oldFn) {
  [...buffers].forEach(b) {
    this.deleteBuffer(b);
  });
  if (oldFn) {
    oldFn();
  }
}(gl.dispose);

You'd need to do the same for textures, renderbuffers, framebuffers, VAOs, programs, shaders

Or you just make helper functions and make your app call those functions that does the same tracking instead of calling functions directly on the context. In other words.

const buffer = customFunctionThatCreatesABufferAndTracksIt(gl);

instead of

const buffer = gl.createBuffer();

License

MIT (see top of js file)

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

webgl-lint

Checks your WebGL usage for common issues
JavaScript
160
star
11

wgpu-matrix

Fast WebGPU 3d math library
JavaScript
129
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