Comparing version 0.5.0 to 0.6.0
614
API.md
@@ -10,7 +10,10 @@ # REGL API | ||
* [Commands](#commands) | ||
+ [Dynamic properties](#dynamic-properties) | ||
+ [Executing commands](#executing-commands) | ||
- [One-shot rendering](#one-shot-rendering) | ||
- [Batch rendering](#batch-rendering) | ||
- [Scoped parameters](#scoped-parameters) | ||
- [Scoped commands](#scoped-commands) | ||
+ [Inputs](#inputs) | ||
- [Context](#context) | ||
- [Props](#props) | ||
- [`this`](#-this-) | ||
+ [Parameters](#parameters) | ||
@@ -35,11 +38,30 @@ - [Shaders](#shaders) | ||
* [Resources](#resources) | ||
+ [Basic usage](#basic-usage) | ||
- [Updating a resource](#updating-a-resource) | ||
- [Destroying a resource](#destroying-a-resource) | ||
+ [Types](#types) | ||
- [Buffers](#buffers) | ||
- [Elements](#elements) | ||
- [Textures](#textures) | ||
- [Render buffers](#render-buffers) | ||
- [Frame buffers](#frame-buffers) | ||
+ [Buffers](#buffers) | ||
- [Constructor](#constructor) | ||
- [Update](#update) | ||
- [Destroy](#destroy) | ||
+ [Elements](#elements) | ||
- [Constructor](#constructor-1) | ||
- [Update](#update-1) | ||
- [Destroy](#destroy-1) | ||
+ [Textures](#textures) | ||
- [Constructor](#constructor-2) | ||
- [Update](#update-2) | ||
- [Destroy](#destroy-2) | ||
+ [Cube maps](#cube-maps) | ||
- [Constructor](#constructor-3) | ||
- [Update](#update-3) | ||
- [Destroy](#destroy-3) | ||
+ [Render buffers](#render-buffers) | ||
- [Constructor](#constructor-4) | ||
- [Update](#update-4) | ||
- [Destroy](#destroy-4) | ||
+ [Frame buffers](#frame-buffers) | ||
- [Constructor](#constructor-5) | ||
- [Update](#update-5) | ||
- [Destroy](#destroy-5) | ||
+ [Cubic frame buffers](#cubic-frame-buffers) | ||
- [Constructor](#constructor-6) | ||
- [Update](#update-6) | ||
- [Destroy](#destroy-6) | ||
* [Other features](#other-features) | ||
@@ -49,6 +71,12 @@ + [Clear the draw buffer](#clear-the-draw-buffer) | ||
+ [Per-frame callbacks](#per-frame-callbacks) | ||
+ [Frame stats](#frame-stats) | ||
+ [Limits](#limits) | ||
+ [Device capabilities and limits](#device-capabilities-and-limits) | ||
+ [Performance metrics](#performance-metrics) | ||
+ [Clean up](#clean-up) | ||
+ [Context loss](#context-loss) | ||
+ [Unsafe escape hatch](#unsafe-escape-hatch) | ||
* [Tips](#tips) | ||
+ [Reuse resources (buffers, elements, textures, etc.)](#reuse-resources--buffers--elements--textures--etc-) | ||
+ [Preallocate memory](#preallocate-memory) | ||
+ [Debug vs release](#debug-vs-release) | ||
+ [Context loss mitigation](#context-loss-mitigation) | ||
@@ -116,3 +144,3 @@ --------------------------------------- | ||
attributes: { | ||
position: regl.buffer([[0, -1], [-1, 0], [1, 1]]) | ||
position: [[0, -1], [-1, 0], [1, 1]] | ||
}, | ||
@@ -129,7 +157,55 @@ | ||
``` | ||
--------------------------------------- | ||
### Dynamic properties | ||
Some parameters can be made dynamic by passing in a function, | ||
### Executing commands | ||
There are 3 ways to execute a command, | ||
#### One-shot rendering | ||
In one shot rendering the command is executed once and immediately, | ||
```javascript | ||
// Executes command immediately with no arguments | ||
command() | ||
// Executes a command using the specified arguments | ||
command(props) | ||
``` | ||
#### Batch rendering | ||
A command can also be executed multiple times by passing a non-negative integer or an array as the first argument. The `batchId` is initially `0` and incremented for each executed, | ||
```javascript | ||
// Executes the command `count`-times | ||
command(count) | ||
// Executes the command once for each args | ||
command([props0, props1, props2, ..., propsn]) | ||
``` | ||
#### Scoped commands | ||
Commands can be nested using scoping. If the argument to the command is a function then the command is evaluated and the state variables are saved as the defaults for all commands executed within its scope, | ||
```javascript | ||
command(function (context) { | ||
// ... execute sub commands | ||
}) | ||
command(props, function (context) { | ||
// ... execute sub commands | ||
}) | ||
``` | ||
--------------------------------------- | ||
### Inputs | ||
Inputs to `regl` commands can come from one of three sources, | ||
* Context: Context variables are not used directly in commands, but can be passed into | ||
* Props: props are arguments which are passed into commands | ||
* `this`: `this` variables are indexed from the `this` variable that the command was called with | ||
If you are familiar with Facebook's [react](https://github.com/facebook/react), these are roughly analogous to a component's [context](https://facebook.github.io/react/docs/context.html), [props](https://facebook.github.io/react/docs/transferring-props.html) and [state](https://facebook.github.io/react/docs/component-api.html#setstate) variables respectively. | ||
#### Example | ||
```javascript | ||
var drawSpinningStretchyTriangle = regl({ | ||
@@ -143,7 +219,8 @@ frag: ` | ||
attribute vec2 position; | ||
uniform float angle, scale; | ||
uniform float angle, scale, width, height; | ||
void main() { | ||
float aspect = width / height; | ||
gl_Position = vec4( | ||
scale * (cos(angle) * position.x - sin(angle) * position.y), | ||
scale * (sin(angle) * position.x + cos(angle) * position.y), | ||
aspect * scale * (sin(angle) * position.x + cos(angle) * position.y), | ||
0, | ||
@@ -154,3 +231,3 @@ 1.0); | ||
attributes: { | ||
position: regl.buffer([[0, -1], [-1, 0], [1, 1]]) | ||
position: [[0, -1], [-1, 0], [1, 1]] | ||
}, | ||
@@ -162,7 +239,7 @@ | ||
// | ||
// * args: which is a user specified object | ||
// * context: which contains data about the current regl environment | ||
// * props: which are user specified arguments | ||
// * batchId: which is the index of the draw command in the batch | ||
// * stats: which are frame stats (see below) | ||
// | ||
angle: function (args, batchId, stats) { | ||
angle: function (context, props, batchId) { | ||
return args.speed * stats.count + 0.01 * batchId | ||
@@ -174,10 +251,21 @@ }, | ||
// | ||
scale: regl.prop('scale') | ||
scale: regl.prop('scale'), | ||
// | ||
// is semantically equivalent to | ||
// | ||
// scale: function (args) { | ||
// return args.scale | ||
// scale: function (context, props) { | ||
// return props.scale | ||
// } | ||
// | ||
// Similarly there are shortcuts for accessing context variables | ||
width: regl.context('viewportWidth'), | ||
height: regl.context('viewportHeight'), | ||
// | ||
// which is the same as writing: | ||
// | ||
// width: function (context) { | ||
// return context.viewportWidth | ||
// } | ||
// | ||
}, | ||
@@ -215,40 +303,136 @@ | ||
For more info on the frame stats [check out the below section](#frame-stats). | ||
#### Context | ||
Context variables in `regl` are computed before any other parameters and can also be passed from a scoped command to any sub-commands. `regl` defines the following default context variables: | ||
--------------------------------------- | ||
### Executing commands | ||
There are 3 ways to execute a command, | ||
| Name | Description | | ||
|------|-------------| | ||
| `frameCount` | The number of frames rendered | | ||
| `deltaTime` | Time since the last frame was rendered in seconds | | ||
| `time` | Total time elapsed since the regl was initialized in seconds | | ||
| `viewportWidth` | Width of the current viewport in pixels | | ||
| `viewportHeight` | Height of the current viewport in pixels | | ||
| `framebufferWidth` | Width of the current framebuffer in pixels | | ||
| `framebufferHeight` | Height of the current framebuffer in pixels | | ||
| `drawingBufferWidth` | Width of the WebGL context drawing buffer | | ||
| `drawingBufferHeight` | Height of the WebGL context drawing buffer | | ||
| `pixelRatio` | The pixel ratio of the drawing buffer | | ||
#### One-shot rendering | ||
In one shot rendering the command is executed once and immediately, | ||
You can define context variables in the `context` block of a command. For example, here is how you can use context variables to set up a camera: | ||
```javascript | ||
// Executes command immediately with no arguments | ||
command() | ||
// This scoped command sets up the camera parameters | ||
var setupCamera = regl({ | ||
context: { | ||
projection: function (context) { | ||
return mat4.perspective([], | ||
Math.PI / 4, | ||
context.viewportWidth / context.viewportHeight, | ||
0.01, | ||
1000.0) | ||
}, | ||
// Executes a command using the specified arguments | ||
command(args) | ||
view: function (context, props) { | ||
return mat4.lookAt([], | ||
props.eye, | ||
props.target, | ||
[0, 1, 0]) | ||
}, | ||
eye: regl.props('eye') | ||
}, | ||
uniforms: { | ||
view: regl.context('view'), | ||
invView: function (context) { | ||
return mat4.inverse([], context.view) | ||
}, | ||
projection: regl.context('projection') | ||
} | ||
}) | ||
// ... do stuff | ||
// In the render function: | ||
setupCamera({ | ||
eye: [10, 0, 0], | ||
target: [0, 0, 0] | ||
}, function () { | ||
// draw stuff | ||
}) | ||
``` | ||
#### Batch rendering | ||
A command can also be executed multiple times by passing a non-negative integer or an array as the first argument. The `batchId` is initially `0` and incremented for each executed, | ||
#### Props | ||
The most common way to pass data into regl is via props. The props for a render command are declared | ||
#### `this` | ||
While `regl` strives to provide a stateless API, there are a few cases where it can be useful to cache state locally to a specific command. One way to achieve this is to use objects. When a regl command is executed as a member function of an object, the `this` parameter is set to the object on which it was called and is passed to all computed parameters. For example, this shows how to use regl to create a simple reusable mesh object, | ||
```javascript | ||
// Executes the command `count`-times | ||
command(count) | ||
// First we create a constructor | ||
function Mesh (center, {positions, cells}) { | ||
this.center = center | ||
this.positions = regl.buffer(positions) | ||
this.cells = regl.buffer(cells) | ||
} | ||
// Executes the command once for each args | ||
command([args0, args1, args2, ..., argsn]) | ||
// Then we assign regl commands directly to the prototype of the class | ||
Mesh.prototype.draw = regl({ | ||
vert: ` | ||
uniform mat4 projection, view, model; | ||
attribute vec3 position; | ||
void main () { | ||
gl_Position = projection * view * model * vec4(position, 1); | ||
}`, | ||
frag: ` | ||
void main () { | ||
gl_FragColor = vec4(1, 0, 0, 1); | ||
}`, | ||
uniforms: { | ||
// dynamic properties are invoked with the same `this` as the command | ||
model: function () => { | ||
var c = this.center | ||
return [ | ||
1, 0, 0, 0, | ||
0, 1, 0, 0, | ||
0, 0, 1, 0, | ||
-c[0], -c[1], -c[2], 1 | ||
] | ||
}, | ||
view: regl.prop('view'), | ||
projection: regl.prop('projection') | ||
} | ||
attributes: { | ||
// here we are using 'positions' proeprty of the mesh | ||
position: regl.this('positions') | ||
}, | ||
// and same for the cells | ||
elements: regl.this('cells') | ||
}) | ||
``` | ||
#### Scoped parameters | ||
Commands can be nested using scoping. If the argument to the command is a function then the command is evaluated and the state variables are saved as the defaults for all commands executed within its scope, | ||
Once defined, we could then use these mesh objects as follows: | ||
```javascript | ||
command(function () { | ||
// ... execute sub commands | ||
// Initialize meshes | ||
var bunnyMesh = new Mesh([5, 2, 1], require('bunny')) | ||
var teapotMesh = new Mesh([0, -3, 0], require('teapot')) | ||
// ... set up rest of scene, compute matrices etc. | ||
var view, projection | ||
// Now draw meshes: | ||
bunnyMesh.draw({ | ||
view: view, | ||
projection: projection | ||
}) | ||
command(args, function () { | ||
// ... execute sub commands | ||
teapotMesh.draw({ | ||
view: view, | ||
projection: projection | ||
}) | ||
@@ -259,3 +443,3 @@ ``` | ||
### Parameters | ||
The input to a command declaration is a complete description of the WebGL state machine. | ||
The input to a command declaration is a complete description of the WebGL state machine in the form of an object. The properties of this object are parameters which specify how values in the WebGL state machine are to be computed. | ||
@@ -290,4 +474,2 @@ --------------------------------------- | ||
**Note** Dynamic shaders are not allowed | ||
**Related WebGL APIs** | ||
@@ -350,8 +532,3 @@ | ||
attributes: { | ||
position: regl.buffer([ | ||
0, 1, 2, | ||
2, 3, 4, | ||
... | ||
]), | ||
// Attributes can be declared explicitly | ||
normals: { | ||
@@ -364,7 +541,18 @@ buffer: regl.buffer([ | ||
normalized: false, | ||
divisor: 0, | ||
size: 0 | ||
// divisor is only used if instancing is enabled | ||
divisor: 0 | ||
}, | ||
color: [1, 0, 1, 1] | ||
// A regl.buffer or the arguments to regl.buffer may also be specified | ||
position: [ | ||
0, 1, 2, | ||
2, 3, 4, | ||
... | ||
], | ||
// Finally, attributes may be initialized with a constant value | ||
color: { | ||
constant: [1, 0, 1, 1] | ||
} | ||
}, | ||
@@ -390,2 +578,3 @@ | ||
* If a buffer is passed for an attribute then all pointer info is inferred | ||
* If the arguments to `regl.buffer` are passed, then a buffer is constructed | ||
* If an array is passed to an attribute, then the vertex attribute is set to a constant | ||
@@ -410,4 +599,4 @@ * `divisor` is only supported if the `ANGLE_instanced_arrays` extension is available | ||
primitive: 'triangles', | ||
count: 6, | ||
offset: 0, | ||
count: 6 | ||
}) | ||
@@ -427,2 +616,3 @@ ``` | ||
* If `elements` is specified while `primitive`, `count` and `offset` are not, then these values may be inferred from the state of the element array buffer. | ||
* `elements` must be either an instance of `regl.elements` or else the arguments to `regl.elements` | ||
* `instances` is only applicable if the `ANGLE_instanced_arrays` extension is present. | ||
@@ -450,5 +640,20 @@ * `primitive` can take on the following values | ||
#### Render target | ||
A `regl.framebuffer` object may also be specified to allow for rendering to offscreen locations. | ||
TODO | ||
```javascript | ||
var command = regl({ | ||
framebuffer: fbo | ||
}) | ||
``` | ||
**Notes** | ||
* `framebuffer` must be a `regl.framebuffer` object | ||
* Passing `null` sets the framebuffer to the drawing buffer | ||
* Updating the render target will modify the viewport | ||
**Related WebGL APIs** | ||
* [`gl.bindFramebuffer`](https://www.opengl.org/sdk/docs/man4/html/glBindFramebuffer.xhtml) | ||
--------------------------------------- | ||
@@ -910,2 +1115,3 @@ #### Depth buffer | ||
* Like `scissor.box`, `viewport` is a bounding box with properties `x,y,w,h` | ||
* Updating `viewport` will modify the context variables `viewportWidth` and `viewportHeight` | ||
@@ -920,26 +1126,9 @@ **Relevant WebGL APIs** | ||
### Basic usage | ||
--------------------------------------- | ||
#### Updating a resource | ||
### Buffers | ||
`regl.buffer` wraps WebGL array buffer objects. | ||
```javascript | ||
resource(options) | ||
``` | ||
#### Constructor | ||
--------------------------------------- | ||
#### Destroying a resource | ||
```javascript | ||
resource.destroy() | ||
``` | ||
--------------------------------------- | ||
### Types | ||
--------------------------------------- | ||
#### Buffers | ||
Examples, | ||
```javascript | ||
// Creates an empty length 100 buffer | ||
@@ -980,9 +1169,50 @@ var zeroBuffer = regl.buffer(100) | ||
* [`gl.createBuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCreateBuffer.xml) | ||
* [`gl.deleteBuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteBuffer.xml) | ||
* [`gl.bufferData`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBufferData.xml) | ||
#### Update | ||
Reinitialize a buffer in place, we call the buffer as a function: | ||
```javascript | ||
// First we create a buffer | ||
var myBuffer = regl.buffer(5) | ||
// ... do stuff ... | ||
// Now reinitialize myBuffer | ||
myBuffer({ | ||
data: [ | ||
1, 2, 3, 4, 5 | ||
] | ||
}) | ||
``` | ||
The arguments to the update pathway are the same as the constructor. | ||
**Relevant WebGL APIs** | ||
* [`gl.bufferData`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBufferData.xml) | ||
* [`gl.bufferData`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBufferData.xml) | ||
#### Destroy | ||
Calling `.destroy()` on a buffer releases all resources associated to the buffer: | ||
```javascript | ||
// Create a buffer | ||
var myBuffer = regl.buffer(10) | ||
// destroys the buffer | ||
myBuffer.destroy() | ||
``` | ||
* [`gl.deleteBuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteBuffer.xml) | ||
--------------------------------------- | ||
#### Elements | ||
Examples, | ||
### Elements | ||
`regl.elements` wraps WebGL element array buffer objects. Each `regl.elements` object stores a buffer object as well as the primitive type and vertex count. | ||
#### Constructor | ||
```javascript | ||
@@ -1003,7 +1233,7 @@ var triElements = regl.elements([ | ||
|----------|-------------|---------| | ||
| `data` | | `null` | | ||
| `length` | | `0` | | ||
| `usage` | | `'static'` | | ||
| `primitive` | | `'triangles'` | | ||
| `count` | | `0` | | ||
| `data` | The data of the element buffer | `null` | | ||
| `usage` | Usage hint (see `gl.bufferData`) | `'static'` | | ||
| `length` | Length of the element buffer in bytes | `0` * | | ||
| `primitive` | Default primitive type for element buffer | `'triangles'` * | | ||
| `count` | Vertex count for element buffer | `0` * | | ||
@@ -1030,11 +1260,51 @@ * `usage` must take on one of the following values | ||
**Notes** | ||
* `primitive`, `count` and `length` are inferred from from the vertex data | ||
**Relevant WebGL APIs** | ||
* [`gl.createBuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCreateBuffer.xml) | ||
* [`gl.deleteBuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteBuffer.xml) | ||
* [`gl.bufferData`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBufferData.xml) | ||
* [`gl.drawElements`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDrawElements.xml) | ||
#### Update | ||
```javascript | ||
// First we create an element buffer | ||
var myElements = regl.elements({ ... }) | ||
// Then we update it by calling it directly | ||
myElements({ | ||
data: [ | ||
[1, 2, 3], | ||
[0, 2, 1] | ||
] | ||
}) | ||
``` | ||
**Relevant WebGL APIs** | ||
* [`gl.bufferData`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBufferData.xml) | ||
#### Destroy | ||
```javascript | ||
// First we create an element buffer | ||
var myElements = regl.elements({ ... }) | ||
// Calling .destroy() on a | ||
myElements.destroy() | ||
``` | ||
**Relevant WebGL APIs** | ||
* [`gl.deleteBuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteBuffer.xml) | ||
--------------------------------------- | ||
#### Textures | ||
### Textures | ||
#### Constructor | ||
There are many ways to upload data to a texture in WebGL. As with drawing commands, regl consolidates all of these crazy configuration parameters into one function. Here are some examples of how to create a texture, | ||
@@ -1216,3 +1486,2 @@ | ||
* [`gl.createTexture`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCreateTexture.xml) | ||
* [`gl.deleteTexture`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteTexture.xml) | ||
* [`gl.texParameter`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexParameter.xml) | ||
@@ -1226,4 +1495,22 @@ * [`gl.pixelStorei`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) | ||
#### Update | ||
**TODO** | ||
#### Destroy | ||
```javascript | ||
var myTexture = regl.texture({ ... }) | ||
myTexture.destroy() | ||
``` | ||
**Relevant WebGL APIs** | ||
* [`gl.deleteTexture`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteTexture.xml) | ||
--------------------------------------- | ||
#### Cube maps | ||
### Cube maps | ||
#### Constructor | ||
Cube maps follow similar syntax to textures. They are created using `regl.cube()` | ||
@@ -1241,8 +1528,20 @@ | ||
--------------------------------------- | ||
#### Render buffers | ||
#### Update | ||
**TODO** | ||
Example, | ||
#### Destroy | ||
```javascript | ||
cubeMap.destroy() | ||
``` | ||
**Relevant WebGL APIs** | ||
* [`gl.deleteTexture`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteTexture.xml) | ||
--------------------------------------- | ||
### Render buffers | ||
#### Constructor | ||
```javascript | ||
var rb = regl.renderbuffer({ | ||
@@ -1277,4 +1576,20 @@ width: 16, | ||
#### Update | ||
**TODO** | ||
#### Destroy | ||
```javascript | ||
rb.destroy() | ||
``` | ||
**Relevant WebGL APIs** | ||
* [`gl.deleteRenderbuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteRenderbuffer.xml) | ||
--------------------------------------- | ||
#### Frame buffers | ||
### Frame buffers | ||
#### Constructor | ||
Example, | ||
@@ -1301,6 +1616,6 @@ | ||
| `depthTexture` | Toggles whether depth/stencil attachments should be in texture | `false` | | ||
| `colorBuffers` | List of color buffers | | | ||
| `depthBuffer` | | | | ||
| `stencilBuffer` | | | | ||
| `depthStencilBuffer` | | | | ||
| `colorBuffers` | List of color buffer attachments | | | ||
| `depthBuffer` | The depth buffer attachment | | | ||
| `stencilBuffer` | The stencil buffer attachment | | | ||
| `depthStencilBuffer` | The depth-stencil attachment | | | ||
@@ -1334,3 +1649,29 @@ | Color format | Description | Attachment | | ||
#### Update | ||
**TODO** | ||
#### Destroy | ||
```javascript | ||
fbo.destroy() | ||
``` | ||
**Relevant WebGL APIs** | ||
* [`gl.deleteFramebuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteFramebuffer.xml) | ||
--------------------------------------- | ||
### Cubic frame buffers | ||
**TODO** | ||
#### Constructor | ||
#### Update | ||
#### Destroy | ||
--------------------------------------- | ||
## Other features | ||
@@ -1376,6 +1717,6 @@ Other than draw commands and resources, there are a few miscellaneous parts of the WebGL API which REGL wraps for completeness. | ||
| `data` | An optional `ArrayBufferView` which gets the result of reading the pixels | `null` | | ||
| `x` | | `0` | | ||
| `y` | | `0` | | ||
| `width` | | viewport.width | | ||
| `height` | | viewport.height | | ||
| `x` | The x-offset of the upper-left corner of the rectangle in pixels | `0` | | ||
| `y` | The y-offset of the upper-left corner of the rectangle in pixels | `0` | | ||
| `width` | The width of the rectangle in pixels | viewport.width | | ||
| `height` | The height of the rectangle in pixels | viewport.height | | ||
@@ -1393,3 +1734,6 @@ **Relevant WebGL APIs** | ||
// Hook a callback to execute each frame | ||
var tick = regl.frame(function (count) { | ||
var tick = regl.frame(function (context) { | ||
// context is the default state of the regl context variables | ||
// ... | ||
@@ -1403,17 +1747,3 @@ }) | ||
--------------------------------------- | ||
### Frame stats | ||
`regl` also tracks a few simple performance and timing stats to simplify benchmarks and animations. These are all accessible via the `regl.stats` object, | ||
| Property | Description | | ||
|----------|-------------| | ||
| `width` | Width of the drawing buffer | | ||
| `height` | Height of the drawing buffer | | ||
| `count` | Total number frames rendered | | ||
| `start` | Wall clock time when `regl` was started | | ||
| `t` | Time of last `frame()` event | | ||
| `dt` | Time between last two `frame()` events | | ||
| `renderTime` | Time spent rendering last frame | | ||
--------------------------------------- | ||
### Limits | ||
### Device capabilities and limits | ||
regl exposes info about the WebGL context limits and capabilities via the `regl.limits` object. The following properties are supported, | ||
@@ -1454,3 +1784,9 @@ | ||
--------------------------------------- | ||
### Performance metrics | ||
**TODO** | ||
--------------------------------------- | ||
### Clean up | ||
When a `regl` context is no longer needed, it can be destroyed releasing all associated resources with the following command: | ||
@@ -1464,1 +1800,35 @@ ```javascript | ||
`regl` makes a best faith effort to handle context loss by default. This means that buffers and textures are reinitialized on a context restore with their contents. | ||
**TODO** | ||
--------------------------------------- | ||
### Unsafe escape hatch | ||
**WARNING**: `regl` is designed in such a way that you should never have to directly access the underlying WebGL context. However, if you really absolutely need to do this for some reason (for example to interface with an external library), you can still get a reference to the WebGL context. Note though that if you do this you will need to restore the `regl` state in order to prevent rendering errors. This can be done with the following unsafe methods: | ||
```javascript | ||
// This retrieves a reference to the underlying WebGL context (don't do this!) | ||
var gl = regl._gl | ||
// ... do some crazy direct manipulation here | ||
// now restore the regl state | ||
regl._refresh() | ||
// Resume using regl as normal | ||
``` | ||
Note that you must call `regl._refresh()` if you have changed the WebGL state. | ||
--------------------------------------- | ||
## Tips | ||
### Reuse resources (buffers, elements, textures, etc.) | ||
### Preallocate memory | ||
### Debug vs release | ||
* Debug mode inserts many checks | ||
* Compiling in release mode removes these assertions, improves performance and reduces bundle size | ||
### Context loss mitigation |
@@ -5,14 +5,6 @@ # Release notes | ||
* Support fixed scopes as arguments | ||
* Support directly constructing elements and attributes from arrays | ||
* Code generation rewrite | ||
+ per-batch contexts | ||
+ Allow for batch scopes | ||
+ Stop using stacks for managing states, save state locally in command | ||
* Error reporting | ||
+ All error messages should link to command/resource declaration | ||
+ Improve validation of vertex attributes | ||
+ Improve validation of dynamic properties | ||
* Optimize buffer creation, reduce memory allocation | ||
* Pool stream buffers | ||
* Resource API improvements | ||
+ Support dynamic variables (context and props) in resource constructors | ||
+ Add in place update methods to buffers and textures | ||
@@ -25,10 +17,7 @@ + Add support for polling buffers and animated GIFs (useful for web audio) | ||
* Cubic frame buffer objects | ||
* WebVR support and integration (need to think how this will work) | ||
* Performance monitoring hooks | ||
* WebVR integration (need to think how this will work) | ||
* Documentation | ||
+ Write "regl for react programmers" | ||
+ Rewrite resource section, bring individual resources to the top | ||
* Code quality and management | ||
+ Refactor attributeState, some names are inconsistent and code is too complex | ||
+ Update development documentation | ||
+ Add contributing guidelines and code of conduct | ||
* Testing | ||
@@ -47,8 +36,2 @@ + Instancing | ||
+ Create some more typical drawing examples | ||
* Optimization | ||
+ Save environment and this variables across batch mode invocations | ||
+ Kill all VM bailouts on all major platforms | ||
+ Kill all garbage collection | ||
+ Optimize generated code | ||
+ Optimize bundle size, remove string constants | ||
* Support more DDS texture formats (HDR, PVRTC, etc.) | ||
@@ -70,2 +53,29 @@ * Build a website (@freeman-lab is on it!) | ||
## 0.6.0 | ||
* Allow for dynamic properties in viewport, scissor box and attributes | ||
* Switch order of arguments to dynamic functions, from (props, context) to (context, props) | ||
+ functions without a props argument become batch static | ||
* Implement non-batch constant context, framebuffer and viewport | ||
* Batched scope rendering | ||
* Switch order of props and context variables for dynamic function args | ||
* function invocation now takes batch id as separate parameter | ||
* Support directly constructing elements and attributes from arrays | ||
* Allow individual attribute properties to be dynamic (eg buffers, offsets, etc.) | ||
* Code generation rewrite | ||
+ State flag polling is now inlined | ||
+ draw and batch inlined for static shaders | ||
+ constants are inlined | ||
+ fewer arguments passed to generated code | ||
+ Stop using separate arrays for stacks to manage state, instead state is saved onto the call stack | ||
* Error reporting | ||
+ All error messages should link to command/resource declaration | ||
+ Improve validation of vertex attributes | ||
+ Improve validation of dynamic properties | ||
* Code quality and contributing | ||
+ Combined lib/state.js and lib/compile.js into lib/core.js | ||
+ Delete most of lib/attribute.js | ||
+ Update development documentation | ||
* Expose limits for shader precision | ||
## 0.5.0 | ||
@@ -72,0 +82,0 @@ |
@@ -1,6 +0,20 @@ | ||
var glTypes = require('./constants/dtypes.json') | ||
var check = require('./util/check') | ||
var GL_FLOAT = 5126 | ||
function AttributeRecord () { | ||
this.state = 0 | ||
this.x = 0.0 | ||
this.y = 0.0 | ||
this.z = 0.0 | ||
this.w = 0.0 | ||
this.buffer = null | ||
this.size = 0 | ||
this.normalized = false | ||
this.type = GL_FLOAT | ||
this.offset = 0 | ||
this.stride = 0 | ||
this.divisor = 0 | ||
} | ||
module.exports = function wrapAttributeState ( | ||
@@ -12,58 +26,2 @@ gl, | ||
stringStore) { | ||
var attributeState = {} | ||
function AttributeRecord () { | ||
this.pointer = false | ||
this.x = 0.0 | ||
this.y = 0.0 | ||
this.z = 0.0 | ||
this.w = 0.0 | ||
this.buffer = null | ||
this.size = 0 | ||
this.normalized = false | ||
this.type = GL_FLOAT | ||
this.offset = 0 | ||
this.stride = 0 | ||
this.divisor = 0 | ||
} | ||
function attributeRecordsEqual (left, right, size) { | ||
if (!left.pointer) { | ||
return !right.pointer && | ||
left.x === right.x && | ||
left.y === right.y && | ||
left.z === right.z && | ||
left.w === right.w | ||
} else { | ||
return right.pointer && | ||
left.buffer === right.buffer && | ||
left.size === size && | ||
left.normalized === right.normalized && | ||
left.type === (right.type || right.buffer.dtype || GL_FLOAT) && | ||
left.offset === right.offset && | ||
left.stride === right.stride && | ||
left.divisor === right.divisor | ||
} | ||
} | ||
function setAttributeRecord (left, right, size) { | ||
var pointer = left.pointer = right.pointer | ||
if (pointer) { | ||
left.buffer = right.buffer | ||
left.size = size | ||
left.normalized = right.normalized | ||
left.type = right.type || right.buffer.dtype || GL_FLOAT | ||
left.offset = right.offset | ||
left.stride = right.stride | ||
left.divisor = right.divisor | ||
} else { | ||
left.x = right.x | ||
left.y = right.y | ||
left.z = right.z | ||
left.w = right.w | ||
} | ||
} | ||
var NUM_ATTRIBUTES = limits.maxAttributes | ||
@@ -75,132 +33,7 @@ var attributeBindings = new Array(NUM_ATTRIBUTES) | ||
function AttributeStack (name) { | ||
this.records = [] | ||
this.name = name | ||
} | ||
function stackTop (stack) { | ||
var records = stack.records | ||
return records[records.length - 1] | ||
} | ||
// =================================================== | ||
// BIND AN ATTRIBUTE | ||
// =================================================== | ||
function bindAttributeRecord (index, current, next, insize) { | ||
var size = next.size || insize | ||
if (attributeRecordsEqual(current, next, size)) { | ||
return | ||
} | ||
if (!next.pointer) { | ||
gl.disableVertexAttribArray(index) | ||
gl.vertexAttrib4f(index, next.x, next.y, next.z, next.w) | ||
} else { | ||
gl.enableVertexAttribArray(index) | ||
next.buffer.bind() | ||
gl.vertexAttribPointer( | ||
index, | ||
size, | ||
next.type || next.buffer.dtype || GL_FLOAT, | ||
next.normalized, | ||
next.stride, | ||
next.offset) | ||
var extInstancing = extensions.angle_instanced_arrays | ||
if (extInstancing) { | ||
extInstancing.vertexAttribDivisorANGLE(index, next.divisor) | ||
} | ||
} | ||
setAttributeRecord(current, next, size) | ||
} | ||
function bindAttribute (index, current, attribStack, size) { | ||
bindAttributeRecord(index, current, stackTop(attribStack), size) | ||
} | ||
// =================================================== | ||
// DEFINE A NEW ATTRIBUTE | ||
// =================================================== | ||
function defAttribute (name) { | ||
var id = stringStore.id(name) | ||
var result = attributeState[id] | ||
if (!result) { | ||
result = attributeState[id] = new AttributeStack(name) | ||
} | ||
return result | ||
} | ||
function createAttributeBox (name) { | ||
var stack = [new AttributeRecord()] | ||
check.saveCommandRef(stack) | ||
function alloc (data) { | ||
var box | ||
if (stack.length <= 0) { | ||
box = new AttributeRecord() | ||
} else { | ||
box = stack.pop() | ||
} | ||
if (typeof data === 'number') { | ||
box.pointer = false | ||
box.x = data | ||
box.y = 0 | ||
box.z = 0 | ||
box.w = 0 | ||
} else if (Array.isArray(data)) { | ||
box.pointer = false | ||
box.x = data[0] | ||
box.y = data[1] | ||
box.z = data[2] | ||
box.w = data[3] | ||
} else { | ||
var buffer = bufferState.getBuffer(data) | ||
var size = 0 | ||
var stride = 0 | ||
var offset = 0 | ||
var divisor = 0 | ||
var normalized = false | ||
var type = 0 | ||
if (!buffer) { | ||
buffer = bufferState.getBuffer(data.buffer) | ||
check(buffer, 'missing or invalid buffer for attribute "' + | ||
name + '" called from command ' + box._commandRef) | ||
size = data.size || 0 | ||
stride = data.stride || 0 | ||
offset = data.offset || 0 | ||
divisor = data.divisor || 0 | ||
normalized = data.normalized || false | ||
type = 0 | ||
if ('type' in data) { | ||
type = glTypes[data.type] | ||
} | ||
} | ||
box.pointer = true | ||
box.buffer = buffer | ||
box.size = size | ||
box.offset = offset | ||
box.stride = stride | ||
box.divisor = divisor | ||
box.normalized = normalized | ||
box.type = type | ||
} | ||
return box | ||
} | ||
function free (box) { | ||
stack.push(box) | ||
} | ||
return { | ||
alloc: alloc, | ||
free: free | ||
} | ||
} | ||
return { | ||
bindings: attributeBindings, | ||
bind: bindAttribute, | ||
bindRecord: bindAttributeRecord, | ||
def: defAttribute, | ||
box: createAttributeBox, | ||
state: attributeState | ||
Record: AttributeRecord, | ||
scope: {}, | ||
state: attributeBindings | ||
} | ||
} |
@@ -11,2 +11,4 @@ // Array and element buffer creation | ||
var GL_ARRAY_BUFFER = 34962 | ||
var GL_BYTE = 5120 | ||
@@ -71,9 +73,9 @@ var GL_UNSIGNED_BYTE = 5121 | ||
module.exports = function wrapBufferState (gl, unbindBuffer) { | ||
module.exports = function wrapBufferState (gl) { | ||
var bufferCount = 0 | ||
var bufferSet = {} | ||
function REGLBuffer (buffer, type) { | ||
function REGLBuffer (type) { | ||
this.id = bufferCount++ | ||
this.buffer = buffer | ||
this.buffer = null | ||
this.type = type | ||
@@ -92,3 +94,2 @@ this.usage = GL_STATIC_DRAW | ||
function refresh (buffer) { | ||
unbindBuffer(buffer) | ||
if (!gl.isBuffer(buffer.buffer)) { | ||
@@ -104,3 +105,2 @@ buffer.buffer = gl.createBuffer() | ||
check(handle, 'buffer must not be deleted already') | ||
unbindBuffer(buffer) | ||
if (gl.isBuffer(handle)) { | ||
@@ -113,6 +113,19 @@ gl.deleteBuffer(handle) | ||
// TODO Implement pooled allocator for stream buffers | ||
function createStream (data) { | ||
var result = createBuffer({ | ||
data: data, | ||
usage: 'stream' | ||
}, | ||
GL_ARRAY_BUFFER, | ||
false) | ||
return result._buffer | ||
} | ||
function destroyStream (stream) { | ||
destroy(stream) | ||
} | ||
function createBuffer (options, type, deferInit) { | ||
var handle = gl.createBuffer() | ||
var buffer = new REGLBuffer(handle, type) | ||
var buffer = new REGLBuffer(type) | ||
bufferSet[buffer.id] = buffer | ||
@@ -185,3 +198,3 @@ | ||
dtype = dtype || typedArrayCode(data) || GL_FLOAT | ||
dtype = dtype || typedArrayCode(data.data) || GL_FLOAT | ||
dimension = shapeY | ||
@@ -240,2 +253,5 @@ data = transpose( | ||
createStream: createStream, | ||
destroyStream: destroyStream, | ||
clear: function () { | ||
@@ -242,0 +258,0 @@ values(bufferSet).forEach(destroy) |
{ | ||
"points": 0, | ||
"point": 0, | ||
"lines": 1, | ||
"line": 1, | ||
"line loop": 2, | ||
"line strip": 3, | ||
"triangles": 4, | ||
"triangle": 4, | ||
"triangle strip": 5, | ||
"triangle fan": 6 | ||
} |
@@ -20,4 +20,2 @@ var check = require('./util/check') | ||
module.exports = function wrapElementsState (gl, extensions, bufferState) { | ||
var elements = [ null ] | ||
function REGLElementBuffer () { | ||
@@ -51,3 +49,2 @@ this.buffer = null | ||
var usage = 'static' | ||
var byteLength = 0 | ||
if ( | ||
@@ -66,8 +63,5 @@ Array.isArray(options) || | ||
} | ||
if ('length' in options) { | ||
byteLength = options.length | ||
} | ||
} | ||
if (Array.isArray(data) || | ||
(isNDArrayLike(data) && data.dtype === 'array') || | ||
(isNDArrayLike(data) && Array.isArray(data.data)) || | ||
'type' in options) { | ||
@@ -80,4 +74,3 @@ buffer({ | ||
usage: usage, | ||
data: data, | ||
length: byteLength | ||
data: data | ||
}) | ||
@@ -87,4 +80,3 @@ } else { | ||
usage: usage, | ||
data: data, | ||
length: byteLength | ||
data: data | ||
}) | ||
@@ -164,7 +156,21 @@ } | ||
// TODO implemented pooled allocator for element buffer streams | ||
function createElementStream (data) { | ||
return createElements({ | ||
usage: 'stream', | ||
data: data | ||
})._elements | ||
} | ||
function destroyElementStream (elements) { | ||
bufferState.destroyStream(elements.buffer) | ||
} | ||
return { | ||
create: createElements, | ||
elements: elements, | ||
createStream: createElementStream, | ||
destroyStream: destroyElementStream, | ||
getElements: function (elements) { | ||
if (elements && elements._elements instanceof REGLElementBuffer) { | ||
if (typeof elements === 'function' && | ||
elements._elements instanceof REGLElementBuffer) { | ||
return elements._elements | ||
@@ -171,0 +177,0 @@ } |
@@ -47,6 +47,2 @@ var check = require('./util/check') | ||
var GL_BACK = 1029 | ||
var BACK_BUFFER = [GL_BACK] | ||
module.exports = function wrapFBOState ( | ||
@@ -58,2 +54,8 @@ gl, | ||
renderbufferState) { | ||
var framebufferState = { | ||
current: null, | ||
next: null, | ||
dirty: false | ||
} | ||
var statusCode = {} | ||
@@ -150,12 +152,2 @@ statusCode[GL_FRAMEBUFFER_COMPLETE] = 'complete' | ||
function checkFormat (attachment, texFormats, rbFormats) { | ||
if (attachment.texture) { | ||
check.oneOf(attachment.texture._texture.params.internalformat, texFormats, | ||
'unsupported texture format for attachment') | ||
} else { | ||
check.oneOf(attachment.renderbuffer._renderbuffer.format, rbFormats, | ||
'unsupported renderbuffer format for attachment') | ||
} | ||
} | ||
function incRefAndCheckShape (attachment, framebuffer) { | ||
@@ -300,4 +292,2 @@ var width = framebuffer.width | ||
var framebufferSet = {} | ||
var framebufferStack = [null] | ||
var framebufferDirty = true | ||
@@ -325,3 +315,3 @@ function REGLFramebuffer () { | ||
} | ||
framebufferDirty = true | ||
framebufferState.dirty = true | ||
gl.bindFramebuffer(GL_FRAMEBUFFER, framebuffer.framebuffer) | ||
@@ -421,3 +411,3 @@ | ||
var colorAttachment = colorBuffers[i] | ||
checkFormat( | ||
check.framebufferFormat( | ||
colorAttachment, | ||
@@ -525,3 +515,3 @@ colorTextureFormatEnums, | ||
depthBuffer = parseAttachment(options.depthBuffer) | ||
checkFormat( | ||
check.framebufferFormat( | ||
depthBuffer, | ||
@@ -534,3 +524,3 @@ depthTextureFormatEnums, | ||
stencilBuffer = parseAttachment(options.stencilBuffer) | ||
checkFormat( | ||
check.framebufferFormat( | ||
stencilBuffer, | ||
@@ -543,3 +533,3 @@ stencilTextureFormatEnums, | ||
depthStencilBuffer = parseAttachment(options.depthStencilBuffer) | ||
checkFormat( | ||
check.framebufferFormat( | ||
depthStencilBuffer, | ||
@@ -698,44 +688,3 @@ depthStencilTextureFormatEnums, | ||
function poll () { | ||
if (framebufferDirty) { | ||
var top = framebufferStack[framebufferStack.length - 1] | ||
var ext_drawbuffers = extensions.webgl_draw_buffers | ||
if (top) { | ||
gl.bindFramebuffer(GL_FRAMEBUFFER, top.framebuffer) | ||
if (ext_drawbuffers) { | ||
ext_drawbuffers.drawBuffersWEBGL(DRAW_BUFFERS[top.colorAttachments.length]) | ||
} | ||
} else { | ||
gl.bindFramebuffer(GL_FRAMEBUFFER, null) | ||
if (ext_drawbuffers) { | ||
ext_drawbuffers.drawBuffersWEBGL(BACK_BUFFER) | ||
} | ||
} | ||
framebufferDirty = false | ||
} | ||
} | ||
function currentFramebuffer () { | ||
return framebufferStack[framebufferStack.length - 1] | ||
} | ||
return { | ||
top: currentFramebuffer, | ||
dirty: function () { | ||
return framebufferDirty | ||
}, | ||
push: function (next_) { | ||
var next = next_ || null | ||
framebufferDirty = framebufferDirty || (next !== currentFramebuffer()) | ||
framebufferStack.push(next) | ||
return framebufferDirty | ||
}, | ||
pop: function () { | ||
var prev = currentFramebuffer() | ||
framebufferStack.pop() | ||
framebufferDirty = framebufferDirty || (prev !== currentFramebuffer()) | ||
return framebufferDirty | ||
}, | ||
return extend(framebufferState, { | ||
getFramebuffer: function (object) { | ||
@@ -750,7 +699,6 @@ if (typeof object === 'function' && object._reglType === 'framebuffer') { | ||
}, | ||
poll: poll, | ||
create: createFBO, | ||
clear: clearCache, | ||
refresh: refreshCache | ||
} | ||
}) | ||
} |
@@ -8,3 +8,3 @@ var check = require('./util/check') | ||
module.exports = function wrapReadPixels (gl, reglPoll, viewportState) { | ||
module.exports = function wrapReadPixels (gl, reglPoll, context) { | ||
function readPixels (input) { | ||
@@ -31,4 +31,4 @@ var options = input || {} | ||
var y = options.y || 0 | ||
var width = options.width || viewportState.width | ||
var height = options.height || viewportState.height | ||
var width = options.width || context.viewportWidth | ||
var height = options.height || context.viewportHeight | ||
@@ -42,4 +42,4 @@ // Compute size | ||
// Type check | ||
check.isTypedArray(data) | ||
check(data.byteLength >= size, 'data buffer too small') | ||
check.isTypedArray(data, 'data buffer for regl.read() must be a typedarray') | ||
check(data.byteLength >= size, 'data buffer for regl.read() too small') | ||
@@ -46,0 +46,0 @@ // Run read pixels |
@@ -10,15 +10,3 @@ var check = require('./util/check') | ||
function ActiveInfo (name, id, location, info) { | ||
this.name = name | ||
this.id = id | ||
this.location = location | ||
this.info = info | ||
} | ||
module.exports = function wrapShaderState ( | ||
gl, | ||
attributeState, | ||
uniformState, | ||
compileShaderDraw, | ||
stringStore) { | ||
module.exports = function wrapShaderState (gl, stringStore) { | ||
// =================================================== | ||
@@ -30,3 +18,20 @@ // glsl compilation and linking | ||
function getShader (type, id) { | ||
function ActiveInfo (name, id, location, info) { | ||
this.name = name | ||
this.id = id | ||
this.location = location | ||
this.info = info | ||
} | ||
function insertActiveInfo (list, info) { | ||
for (var i = 0; i < list.length; ++i) { | ||
if (list[i].id === info.id) { | ||
list[i].location = info.location | ||
return | ||
} | ||
} | ||
list.push(info) | ||
} | ||
function getShader (type, id, command) { | ||
var cache = type === GL_FRAGMENT_SHADER ? fragShaders : vertShaders | ||
@@ -40,3 +45,3 @@ var shader = cache[id] | ||
gl.compileShader(shader) | ||
check.shaderError(gl, shader, source, type) | ||
check.shaderError(gl, shader, source, type, command) | ||
cache[id] = shader | ||
@@ -54,3 +59,6 @@ } | ||
var PROGRAM_COUNTER = 0 | ||
function REGLProgram (fragId, vertId) { | ||
this.id = PROGRAM_COUNTER++ | ||
this.fragId = fragId | ||
@@ -61,7 +69,5 @@ this.vertId = vertId | ||
this.attributes = [] | ||
this.draw = function () {} | ||
this.batchCache = {} | ||
} | ||
function linkProgram (desc) { | ||
function linkProgram (desc, command) { | ||
var i, info | ||
@@ -83,3 +89,4 @@ | ||
stringStore.str(desc.fragId), | ||
stringStore.str(desc.vertId)) | ||
stringStore.str(desc.vertId), | ||
command) | ||
@@ -90,4 +97,3 @@ // ------------------------------- | ||
var numUniforms = gl.getProgramParameter(program, GL_ACTIVE_UNIFORMS) | ||
var uniforms = desc.uniforms = [] | ||
var uniforms = desc.uniforms | ||
for (i = 0; i < numUniforms; ++i) { | ||
@@ -99,4 +105,3 @@ info = gl.getActiveUniform(program, i) | ||
var name = info.name.replace('[0]', '[' + j + ']') | ||
uniformState.def(name) | ||
uniforms.push(new ActiveInfo( | ||
insertActiveInfo(uniforms, new ActiveInfo( | ||
name, | ||
@@ -108,4 +113,3 @@ stringStore.id(name), | ||
} else { | ||
uniformState.def(info.name) | ||
uniforms.push(new ActiveInfo( | ||
insertActiveInfo(uniforms, new ActiveInfo( | ||
info.name, | ||
@@ -123,8 +127,7 @@ stringStore.id(info.name), | ||
var numAttributes = gl.getProgramParameter(program, GL_ACTIVE_ATTRIBUTES) | ||
var attributes = desc.attributes = [] | ||
var attributes = desc.attributes | ||
for (i = 0; i < numAttributes; ++i) { | ||
info = gl.getActiveAttrib(program, i) | ||
if (info) { | ||
attributeState.def(info.name) | ||
attributes.push(new ActiveInfo( | ||
insertActiveInfo(attributes, new ActiveInfo( | ||
info.name, | ||
@@ -136,13 +139,4 @@ stringStore.id(info.name), | ||
} | ||
// ------------------------------- | ||
// clear cached rendering methods | ||
// ------------------------------- | ||
desc.draw = compileShaderDraw(desc) | ||
desc.batchCache = {} | ||
} | ||
var fragShaderStack = [ -1 ] | ||
var vertShaderStack = [ -1 ] | ||
return { | ||
@@ -169,3 +163,3 @@ clear: function () { | ||
program: function (vertId, fragId) { | ||
program: function (vertId, fragId, command) { | ||
check(vertId >= 0, 'missing vertex shader') | ||
@@ -181,3 +175,3 @@ check(fragId >= 0, 'missing fragment shader') | ||
program = new REGLProgram(fragId, vertId) | ||
linkProgram(program) | ||
linkProgram(program, command) | ||
cache[vertId] = program | ||
@@ -191,5 +185,5 @@ programList.push(program) | ||
frag: fragShaderStack, | ||
vert: vertShaderStack | ||
frag: null, | ||
vert: null | ||
} | ||
} |
@@ -182,3 +182,3 @@ var check = require('./util/check') | ||
module.exports = function createTextureSet (gl, extensions, limits, reglPoll, viewportState) { | ||
module.exports = function createTextureSet (gl, extensions, limits, reglPoll, contextState) { | ||
var mipmapHint = { | ||
@@ -615,4 +615,4 @@ "don't care": GL_DONT_CARE, | ||
this.y = this.y | 0 | ||
this.width = (this.width || viewportState.width) | 0 | ||
this.height = (this.height || viewportState.height) | 0 | ||
this.width = (this.width || contextState.viewportWidth) | 0 | ||
this.height = (this.height || contextState.viewportHeight) | 0 | ||
this.setDefaultFormat() | ||
@@ -619,0 +619,0 @@ } |
@@ -1,2 +0,6 @@ | ||
// Error checking and parameter validation | ||
// Error checking and parameter validation. | ||
// | ||
// Statements for the form `check.someProcedure(...)` get removed by | ||
// a browserify transform for optimized/minified bundles. | ||
// | ||
/* globals btoa */ | ||
@@ -100,8 +104,8 @@ var isTypedArray = require('./is-typed-array') | ||
var error = new Error() | ||
var stack = error.stack | ||
var pat = /at compileProcedure.*\n\s+at.*\((.*)\)/.exec(stack) | ||
var stack = error.stack.toString() | ||
var pat = /compileProcedure.*\n\s*at.*\((.*)\)/.exec(stack) | ||
if (pat) { | ||
return pat[1] | ||
} | ||
var pat2 = /at compileProcedure.*\n\s+at\s+(.*)\n/.exec(stack) | ||
var pat2 = /compileProcedure.*\n\s*at\s+(.*)(\n|$)/.exec(stack) | ||
if (pat2) { | ||
@@ -127,3 +131,3 @@ return pat2[1] | ||
function parseSource (source) { | ||
function parseSource (source, command) { | ||
var lines = source.split('\n') | ||
@@ -136,3 +140,3 @@ var lineNumber = 1 | ||
} | ||
files.unknown.name = files[0].name = guessCommand() | ||
files.unknown.name = files[0].name = command || guessCommand() | ||
files.unknown.lines.push(new ShaderLine(0, '')) | ||
@@ -209,7 +213,7 @@ for (var i = 0; i < lines.length; ++i) { | ||
function checkShaderError (gl, shader, source, type) { | ||
function checkShaderError (gl, shader, source, type, command) { | ||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { | ||
var errLog = gl.getShaderInfoLog(shader) | ||
var typeName = type === gl.FRAGMENT_SHADER ? 'fragment' : 'vertex' | ||
var files = parseSource(source) | ||
var files = parseSource(source, command) | ||
var errors = parseErrorLog(errLog) | ||
@@ -280,7 +284,7 @@ annotateFiles(files, errors) | ||
function checkLinkError (gl, program, fragShader, vertShader) { | ||
function checkLinkError (gl, program, fragShader, vertShader, command) { | ||
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { | ||
var errLog = gl.getProgramInfoLog(program) | ||
var fragParse = parseSource(fragShader) | ||
var vertParse = parseSource(vertShader) | ||
var fragParse = parseSource(fragShader, command) | ||
var vertParse = parseSource(vertShader, command) | ||
@@ -338,41 +342,48 @@ var header = 'Error linking program with vertex shader, "' + | ||
function checkDrawCommandState ( | ||
drawState, | ||
shaderState, | ||
uniformState, | ||
attributeState, | ||
commandRef, | ||
fragId, | ||
vertId, | ||
uniformSet, | ||
attributeSet, | ||
hasCount) { | ||
var i, id | ||
function commandRaise (message, command) { | ||
var callSite = guessCallSite() | ||
raise(message + | ||
' in command ' + (command || guessCommand()) + | ||
(callSite === 'unknown' ? '' : ' called from ' + callSite)) | ||
} | ||
// Check count | ||
if (!hasCount && drawState.count[drawState.count.length - 1] < 0) { | ||
raise('missing vertex count in command ' + commandRef + ' - called from ' + guessCallSite()) | ||
function checkCommand (pred, message, command) { | ||
if (!pred) { | ||
commandRaise(message, command || guessCommand()) | ||
} | ||
} | ||
var program = shaderState.program( | ||
vertId || shaderState.vert[shaderState.vert.length - 1], | ||
fragId || shaderState.frag[shaderState.frag.length - 1]) | ||
function checkParameterCommand (param, possibilities, message, command) { | ||
if (!(param in possibilities)) { | ||
commandRaise( | ||
'unknown parameter (' + param + ')' + encolon(message) + | ||
'. possible values: ' + Object.keys(possibilities).join(), | ||
command || guessCommand()) | ||
} | ||
} | ||
// Check uniforms | ||
var progUniforms = program.uniforms | ||
for (i = 0; i < progUniforms.length; ++i) { | ||
id = progUniforms[i].id | ||
if (!uniformSet[id] && uniformState.uniforms[id].length <= 0) { | ||
raise('missing uniform "' + progUniforms[i].name + '" in command ' + | ||
commandRef + ' called from ' + guessCallSite()) | ||
} | ||
function checkCommandType (value, type, message, command) { | ||
if (typeof value !== type) { | ||
commandRaise( | ||
'invalid parameter type' + encolon(message) + | ||
'. expected ' + type + ', got ' + (typeof value), | ||
command || guessCommand()) | ||
} | ||
} | ||
var progAttributes = program.attributes | ||
for (i = 0; i < progAttributes.length; ++i) { | ||
id = progAttributes[i].id | ||
if (!attributeSet[id] && attributeState.state[id].records.length <= 0) { | ||
raise('missing attribute "' + progAttributes[i].name + '" in command ' + | ||
commandRef + ' called from ' + guessCallSite()) | ||
} | ||
function checkOptional (block) { | ||
block() | ||
} | ||
function checkFramebufferFormat (attachment, texFormats, rbFormats) { | ||
if (attachment.texture) { | ||
checkOneOf( | ||
attachment.texture._texture.params.internalformat, | ||
texFormats, | ||
'unsupported texture format for attachment') | ||
} else { | ||
checkOneOf( | ||
attachment.renderbuffer._renderbuffer.format, | ||
rbFormats, | ||
'unsupported renderbuffer format for attachment') | ||
} | ||
@@ -382,5 +393,10 @@ } | ||
module.exports = extend(check, { | ||
optional: checkOptional, | ||
raise: raise, | ||
commandRaise: commandRaise, | ||
command: checkCommand, | ||
parameter: checkParameter, | ||
commandParameter: checkParameterCommand, | ||
type: checkTypeOf, | ||
commandType: checkCommandType, | ||
isTypedArray: checkIsTypedArray, | ||
@@ -393,4 +409,5 @@ nni: checkNonNegativeInt, | ||
saveCommandRef: saveCommandRef, | ||
drawOk: checkDrawCommandState, | ||
saveDrawInfo: saveDrawCommandInfo | ||
saveDrawInfo: saveDrawCommandInfo, | ||
framebufferFormat: checkFramebufferFormat, | ||
guessCommand: guessCommand | ||
}) |
@@ -7,2 +7,6 @@ var extend = require('./extend') | ||
function join (x) { | ||
return slice(x).join('') | ||
} | ||
module.exports = function createEnvironment () { | ||
@@ -18,2 +22,8 @@ // Unique variable id counter | ||
function link (value) { | ||
for (var i = 0; i < linkedValues.length; ++i) { | ||
if (linkedValues[i] === value) { | ||
return linkedNames[i] | ||
} | ||
} | ||
var name = 'g' + (varCounter++) | ||
@@ -49,6 +59,6 @@ linkedNames.push(name) | ||
toString: function () { | ||
return [ | ||
return join([ | ||
(vars.length > 0 ? 'var ' + vars + ';' : ''), | ||
code.join('') | ||
].join('') | ||
join(code) | ||
]) | ||
} | ||
@@ -58,8 +68,65 @@ }) | ||
function scope () { | ||
var entry = block() | ||
var exit = block() | ||
var entryToString = entry.toString | ||
var exitToString = exit.toString | ||
function save (object, prop) { | ||
exit(object, prop, '=', entry.def(object, prop), ';') | ||
} | ||
return extend(entry, { | ||
entry: entry, | ||
exit: exit, | ||
save: save, | ||
set: function (object, prop, value) { | ||
save(object, prop) | ||
entry(object, prop, '=', value, ';') | ||
}, | ||
toString: function () { | ||
return entryToString() + exitToString() | ||
} | ||
}) | ||
} | ||
function conditional () { | ||
var pred = join(arguments) | ||
var thenBlock = scope() | ||
var elseBlock = scope() | ||
var thenToString = thenBlock.toString | ||
var elseToString = elseBlock.toString | ||
return extend(thenBlock, { | ||
then: function () { | ||
thenBlock.apply(thenBlock, slice(arguments)) | ||
return this | ||
}, | ||
else: function () { | ||
elseBlock.apply(elseBlock, slice(arguments)) | ||
return this | ||
}, | ||
toString: function () { | ||
var elseClause = elseToString() | ||
if (elseClause) { | ||
elseClause = 'else{' + elseClause + '}' | ||
} | ||
return join([ | ||
'if(', pred, '){', | ||
thenToString(), | ||
'}', elseClause | ||
]) | ||
} | ||
}) | ||
} | ||
// procedure list | ||
var globalBlock = block() | ||
var procedures = {} | ||
function proc (name) { | ||
function proc (name, count) { | ||
var args = [] | ||
function arg () { | ||
var name = 'a' + (varCounter++) | ||
var name = 'a' + args.length | ||
args.push(name) | ||
@@ -69,3 +136,8 @@ return name | ||
var body = block() | ||
count = count || 0 | ||
for (var i = 0; i < count; ++i) { | ||
arg() | ||
} | ||
var body = scope() | ||
var bodyToString = body.toString | ||
@@ -76,7 +148,7 @@ | ||
toString: function () { | ||
return [ | ||
return join([ | ||
'function(', args.join(), '){', | ||
bodyToString(), | ||
'}' | ||
].join('') | ||
]) | ||
} | ||
@@ -89,3 +161,5 @@ }) | ||
function compile () { | ||
var code = ['"use strict";return {'] | ||
var code = ['"use strict";', | ||
globalBlock, | ||
'return {'] | ||
Object.keys(procedures).forEach(function (name) { | ||
@@ -95,3 +169,7 @@ code.push('"', name, '":', procedures[name].toString(), ',') | ||
code.push('}') | ||
var proc = Function.apply(null, linkedNames.concat([code.join('')])) | ||
var src = join(code) | ||
.replace(/;/g, ';\n') | ||
.replace(/}/g, '}\n') | ||
.replace(/{/g, '{\n') | ||
var proc = Function.apply(null, linkedNames.concat(src)) | ||
return proc.apply(null, linkedValues) | ||
@@ -101,7 +179,10 @@ } | ||
return { | ||
global: globalBlock, | ||
link: link, | ||
block: block, | ||
proc: proc, | ||
scope: scope, | ||
cond: conditional, | ||
compile: compile | ||
} | ||
} |
{ | ||
"name": "regl", | ||
"version": "0.5.0", | ||
"version": "0.6.0", | ||
"description": "WebGL", | ||
@@ -19,6 +19,5 @@ "main": "regl.js", | ||
"canvas-orbit-camera": "^1.0.2", | ||
"codacy-coverage": "^1.1.3", | ||
"falafel": "^1.2.0", | ||
"faucet": "0.0.1", | ||
"gl": "^3.0.5", | ||
"gl": "^4.0.1", | ||
"gl-mat4": "^1.1.4", | ||
@@ -25,0 +24,0 @@ "glob": "^7.0.3", |
@@ -5,17 +5,6 @@ # regl | ||
This repo is an attempt at building new functional abstractions for working with WebGL. It is still **experimental**, so expect things to change a lot in the near future! If you want to know more about why I am writing this thing and why it looks the way it does, take a look at the [rationale](RATIONALE.md). | ||
`regl` is a fast functional reactive abstraction for WebGL. | ||
### Why use regl | ||
## Example | ||
`regl` offers the following advantages over raw WebGL code: | ||
* **Less state** Draw commands in regl are self contained, so you don't have to worry about some other weird subroutine breaking your rendering code | ||
* **No `bind()`** In regl, shaders, buffers, textures and fbos are specified declaratively, so there is no need to ever `bind()` them or unbind them. | ||
* **Fewer silent failures** If you pass incorrect parameters to some WebGL method, the default behavior is to set an error code and continue on. Because `regl` commands have more structure, we can do more validation up front without the run time performance cost. | ||
* **Sane defaults** Many WebGL APIs have redundant or outright broken parameters (for example `border` in `gl.texImage2D` or `transpose` in `gl.uniformMatrix4fv`). `regl` wraps these APIs in such a way that you will never have to see this mess. | ||
* **Low overhead** regl uses caching and code generation to minimize overhead from | ||
* **Still just WebGL** regl exposes the full power of the WebGL API, no features are removed or hidden. | ||
## Simple example | ||
In `regl`, there are two fundamental abstractions, **resources** and **commands**: | ||
@@ -72,2 +61,3 @@ | ||
// regl.frame() wraps requestAnimationFrame and also handles viewport changes | ||
regl.frame(() => { | ||
@@ -92,3 +82,3 @@ // clear contents of the drawing buffer | ||
## More examples | ||
#### More examples | ||
@@ -123,12 +113,16 @@ [Check out the demo gallery](https://mikolalysenko.github.io/regl/www/gallery.html) | ||
## Comparisons | ||
## Why use `regl`? | ||
`regl` is basically all of WebGL without all of the shared state. You can do anything you could in regular WebGL with little overhead and way less debugging. | ||
TODO implement spinning textured cube in each of the following frameworks | ||
### Comparisons | ||
**TODO** implement spinning textured cube in each of the following frameworks | ||
* vs WebGL | ||
* vs gl-* modules from stack.gl | ||
* gl-react | ||
* vs TWGL | ||
* vs THREE.js | ||
## Benchmarks | ||
### Benchmarks | ||
You can run benchmarks locally using `npm run bench` or check them out here: | ||
@@ -147,7 +141,10 @@ | ||
* [Commands](API.md#commands) | ||
+ [Dynamic properties](API.md#dynamic-properties) | ||
+ [Executing commands](API.md#executing-commands) | ||
- [One-shot rendering](API.md#one-shot-rendering) | ||
- [Batch rendering](API.md#batch-rendering) | ||
- [Scoped parameters](API.md#scoped-parameters) | ||
- [Scoped commands](API.md#scoped-commands) | ||
+ [Inputs](API.md#inputs) | ||
- [Context](API.md#context) | ||
- [Props](API.md#props) | ||
- [`this`](API.md#-this-) | ||
+ [Parameters](API.md#parameters) | ||
@@ -172,11 +169,30 @@ - [Shaders](API.md#shaders) | ||
* [Resources](API.md#resources) | ||
+ [Basic usage](API.md#basic-usage) | ||
- [Updating a resource](API.md#updating-a-resource) | ||
- [Destroying a resource](API.md#destroying-a-resource) | ||
+ [Types](API.md#types) | ||
- [Buffers](API.md#buffers) | ||
- [Elements](API.md#elements) | ||
- [Textures](API.md#textures) | ||
- [Render buffers](API.md#render-buffers) | ||
- [Frame buffers](API.md#frame-buffers) | ||
+ [Buffers](API.md#buffers) | ||
- [Constructor](API.md#constructor) | ||
- [Update](API.md#update) | ||
- [Destroy](API.md#destroy) | ||
+ [Elements](API.md#elements) | ||
- [Constructor](API.md#constructor-1) | ||
- [Update](API.md#update-1) | ||
- [Destroy](API.md#destroy-1) | ||
+ [Textures](API.md#textures) | ||
- [Constructor](API.md#constructor-2) | ||
- [Update](API.md#update-2) | ||
- [Destroy](API.md#destroy-2) | ||
+ [Cube maps](API.md#cube-maps) | ||
- [Constructor](API.md#constructor-3) | ||
- [Update](API.md#update-3) | ||
- [Destroy](API.md#destroy-3) | ||
+ [Render buffers](API.md#render-buffers) | ||
- [Constructor](API.md#constructor-4) | ||
- [Update](API.md#update-4) | ||
- [Destroy](API.md#destroy-4) | ||
+ [Frame buffers](API.md#frame-buffers) | ||
- [Constructor](API.md#constructor-5) | ||
- [Update](API.md#update-5) | ||
- [Destroy](API.md#destroy-5) | ||
+ [Cubic frame buffers](API.md#cubic-frame-buffers) | ||
- [Constructor](API.md#constructor-6) | ||
- [Update](API.md#update-6) | ||
- [Destroy](API.md#destroy-6) | ||
* [Other features](API.md#other-features) | ||
@@ -186,12 +202,19 @@ + [Clear the draw buffer](API.md#clear-the-draw-buffer) | ||
+ [Per-frame callbacks](API.md#per-frame-callbacks) | ||
+ [Frame stats](API.md#frame-stats) | ||
+ [Limits](API.md#limits) | ||
+ [Device capabilities and limits](API.md#device-capabilities-and-limits) | ||
+ [Performance metrics](API.md#performance-metrics) | ||
+ [Clean up](API.md#clean-up) | ||
+ [Context loss](API.md#context-loss) | ||
+ [Unsafe escape hatch](API.md#unsafe-escape-hatch) | ||
* [Tips](API.md#tips) | ||
+ [Reuse resources (buffers, elements, textures, etc.)](API.md#reuse-resources--buffers--elements--textures--etc-) | ||
+ [Preallocate memory](API.md#preallocate-memory) | ||
+ [Debug vs release](API.md#debug-vs-release) | ||
+ [Context loss mitigation](API.md#context-loss-mitigation) | ||
## Contributing | ||
## Development | ||
The latest changes in `regl` can be found in the [CHANGELOG](CHANGES.md). | ||
[For info on how to build and test headless, see the contributing guide here](DEVELOPING.md) | ||
## Credits | ||
### License | ||
All code (c) 2016 MIT License | ||
@@ -201,3 +224,3 @@ | ||
### Asset licenses | ||
#### Asset licenses | ||
Many examples use creative commons or public domain artwork for illustrative purposes. These assets are not included in any of the redistributable packages of regl. | ||
@@ -204,0 +227,0 @@ |
250
regl.js
var check = require('./lib/util/check') | ||
var extend = require('./lib/util/extend') | ||
var dynamic = require('./lib/dynamic') | ||
var raf = require('./lib/util/raf') | ||
var clock = require('./lib/util/clock') | ||
var createStringStore = require('./lib/strings') | ||
var initWebGL = require('./lib/webgl') | ||
var createStringStore = require('./lib/strings') | ||
var wrapExtensions = require('./lib/extension') | ||
@@ -12,12 +15,6 @@ var wrapLimits = require('./lib/limits') | ||
var wrapFramebuffers = require('./lib/framebuffer') | ||
var wrapUniforms = require('./lib/uniform') | ||
var wrapAttributes = require('./lib/attribute') | ||
var wrapShaders = require('./lib/shader') | ||
var wrapDraw = require('./lib/draw') | ||
var wrapContext = require('./lib/state') | ||
var createCompiler = require('./lib/compile') | ||
var wrapRead = require('./lib/read') | ||
var dynamic = require('./lib/dynamic') | ||
var raf = require('./lib/util/raf') | ||
var clock = require('./lib/util/clock') | ||
var createCore = require('./lib/core') | ||
@@ -49,20 +46,31 @@ var GL_COLOR_BUFFER_BIT = 16384 | ||
var viewportState = { | ||
width: gl.drawingBufferWidth, | ||
height: gl.drawingBufferHeight | ||
var START_TIME = clock() | ||
var LAST_TIME = START_TIME | ||
var WIDTH = gl.drawingBufferWidth | ||
var HEIGHT = gl.drawingBufferHeight | ||
var contextState = { | ||
count: 0, | ||
deltaTime: 0, | ||
time: 0, | ||
viewportWidth: WIDTH, | ||
viewportHeight: HEIGHT, | ||
framebufferWidth: WIDTH, | ||
framebufferHeight: HEIGHT, | ||
drawingBufferWidth: WIDTH, | ||
drawingBufferHeight: HEIGHT, | ||
pixelRatio: options.pixelRatio | ||
} | ||
var uniformState = {} | ||
var drawState = { | ||
elements: null, | ||
primitive: 4, // GL_TRIANGLES | ||
count: -1, | ||
offset: 0, | ||
instances: -1 | ||
} | ||
var limits = wrapLimits( | ||
gl, | ||
extensions) | ||
var bufferState = wrapBuffers(gl, unbindBuffer) | ||
var elementState = wrapElements( | ||
gl, | ||
extensions, | ||
bufferState) | ||
var uniformState = wrapUniforms(stringStore) | ||
var limits = wrapLimits(gl, extensions) | ||
var bufferState = wrapBuffers(gl) | ||
var elementState = wrapElements(gl, extensions, bufferState) | ||
var attributeState = wrapAttributes( | ||
@@ -74,17 +82,3 @@ gl, | ||
stringStore) | ||
var shaderState = wrapShaders( | ||
gl, | ||
attributeState, | ||
uniformState, | ||
function (program) { | ||
return compiler.draw(program) | ||
}, | ||
stringStore) | ||
var drawState = wrapDraw( | ||
gl, | ||
extensions, | ||
bufferState) | ||
var shaderState = wrapShaders(gl, stringStore) | ||
var textureState = wrapTextures( | ||
@@ -95,9 +89,4 @@ gl, | ||
poll, | ||
viewportState) | ||
var renderbufferState = wrapRenderbuffers( | ||
gl, | ||
extensions, | ||
limits) | ||
contextState) | ||
var renderbufferState = wrapRenderbuffers(gl, extensions, limits) | ||
var framebufferState = wrapFramebuffers( | ||
@@ -109,37 +98,6 @@ gl, | ||
renderbufferState) | ||
var readPixels = wrapRead(gl, poll, contextState) | ||
var startTime = clock() | ||
var frameState = { | ||
count: 0, | ||
start: startTime, | ||
dt: 0, | ||
t: startTime, | ||
renderTime: 0, | ||
width: gl.drawingBufferWidth, | ||
height: gl.drawingBufferHeight, | ||
pixelRatio: options.pixelRatio | ||
} | ||
var context = { | ||
count: 0, | ||
batchId: 0, | ||
deltaTime: 0, | ||
time: 0, | ||
viewportWidth: frameState.width, | ||
viewportHeight: frameState.height, | ||
drawingBufferWidth: frameState.width, | ||
drawingBufferHeight: frameState.height, | ||
pixelRatio: frameState.pixelRatio | ||
} | ||
var glState = wrapContext( | ||
var core = createCore( | ||
gl, | ||
framebufferState, | ||
viewportState) | ||
var readPixels = wrapRead(gl, poll, viewportState) | ||
var compiler = createCompiler( | ||
gl, | ||
stringStore, | ||
@@ -152,3 +110,2 @@ extensions, | ||
framebufferState, | ||
glState, | ||
uniformState, | ||
@@ -158,17 +115,5 @@ attributeState, | ||
drawState, | ||
context, | ||
poll) | ||
contextState) | ||
function unbindBuffer (buffer) { | ||
for (var i = 0; i < attributeState.bindings.length; ++i) { | ||
var attr = attributeState.bindings[i] | ||
if (attr.pointer && attr.buffer === buffer) { | ||
attr.pointer = false | ||
attr.buffer = null | ||
attr.x = attr.y = attr.z = attr.w = NaN | ||
gl.disableVertexAttribArray(i) | ||
} | ||
} | ||
} | ||
var nextState = core.next | ||
var canvas = gl.canvas | ||
@@ -179,24 +124,30 @@ | ||
function handleRAF () { | ||
// schedule next animation frame | ||
activeRAF = raf.next(handleRAF) | ||
frameState.count += 1 | ||
context.count = frameState.count | ||
if (frameState.width !== gl.drawingBufferWidth || | ||
frameState.height !== gl.drawingBufferHeight) { | ||
context.viewportWidth = | ||
context.drawingBufferWidth = | ||
frameState.width = gl.drawingBufferWidth | ||
context.viewportHeight = | ||
context.drawingBufferHeight = | ||
frameState.height = gl.drawingBufferHeight | ||
glState.notifyViewportChanged() | ||
} | ||
// increment frame coun | ||
contextState.count += 1 | ||
// reset viewport | ||
var viewport = nextState.viewport | ||
var scissorBox = nextState.scissor_box | ||
viewport[0] = viewport[1] = scissorBox[0] = scissorBox[1] = 0 | ||
contextState.viewportWidth = | ||
contextState.frameBufferWidth = | ||
contextState.drawingBufferWidth = | ||
viewport[2] = | ||
scissorBox[2] = gl.drawingBufferWidth | ||
contextState.viewportHeight = | ||
contextState.frameBufferWidth = | ||
contextState.drawingBufferHeight = | ||
viewport[3] = | ||
scissorBox[3] = gl.drawingBufferHeight | ||
var now = clock() | ||
frameState.dt = now - frameState.t | ||
frameState.t = now | ||
contextState.deltaTime = (now - LAST_TIME) / 1000.0 | ||
contextState.time = (now - START_TIME) / 1000.0 | ||
LAST_TIME = now | ||
context.deltaTime = frameState.dt / 1000.0 | ||
context.time = (now - startTime) / 1000.0 | ||
core.procs.refresh() | ||
textureState.poll() | ||
@@ -206,5 +157,4 @@ | ||
var cb = rafCallbacks[i] | ||
cb(null, context) | ||
cb(contextState, null, 0) | ||
} | ||
frameState.renderTime = clock() - now | ||
} | ||
@@ -236,2 +186,3 @@ | ||
extensionState.refresh() | ||
core.procs.refresh() | ||
bufferState.refresh() | ||
@@ -242,3 +193,2 @@ textureState.refresh() | ||
shaderState.refresh() | ||
glState.refresh() | ||
if (options.onContextRestored) { | ||
@@ -278,4 +228,2 @@ options.onContextRestored() | ||
var hasDynamic = false | ||
function flattenNestedOptions (options) { | ||
@@ -313,3 +261,2 @@ var result = extend({}, options) | ||
if (dynamic.isDynamic(value)) { | ||
hasDynamic = true | ||
dynamicItems[option] = dynamic.unbox(value, option) | ||
@@ -332,6 +279,3 @@ } else { | ||
var compiled = compiler.command( | ||
opts.static, uniforms.static, attributes.static, | ||
opts.dynamic, uniforms.dynamic, attributes.dynamic, | ||
context, hasDynamic) | ||
var compiled = core.compile(opts, attributes, uniforms, context) | ||
@@ -350,30 +294,31 @@ var draw = compiled.draw | ||
check.saveDrawInfo(opts, uniforms, attributes, stringStore) | ||
function REGLCommand (args, body) { | ||
var i | ||
if (typeof args === 'function') { | ||
return scope.call(this, null, args) | ||
return scope.call(this, null, args, 0) | ||
} else if (typeof body === 'function') { | ||
return scope.call(this, args, body) | ||
} | ||
// Runtime shader check. Removed in production builds | ||
check.drawOk( | ||
drawState, | ||
shaderState, | ||
uniformState, | ||
attributeState, | ||
opts._commandRef, | ||
opts._fragId, | ||
opts._vertId, | ||
opts._uniformSet, | ||
opts._attributeSet, | ||
opts._hasCount) | ||
if (typeof args === 'number') { | ||
return batch.call(this, args | 0, reserve(args | 0)) | ||
if (typeof args === 'number') { | ||
for (i = 0; i < args; ++i) { | ||
scope.call(this, null, body, i) | ||
} | ||
return | ||
} else if (Array.isArray(args)) { | ||
for (i = 0; i < args.length; ++i) { | ||
scope.call(this, args[i], body, i) | ||
} | ||
return | ||
} else { | ||
return scope.call(this, args, body, 0) | ||
} | ||
} else if (typeof args === 'number') { | ||
if (args > 0) { | ||
return batch.call(this, reserve(args | 0), args | 0) | ||
} | ||
} else if (Array.isArray(args)) { | ||
return batch.call(this, args.length, args) | ||
if (args.length) { | ||
return batch.call(this, args, args.length) | ||
} | ||
} else { | ||
return draw.call(this, args) | ||
} | ||
return draw.call(this, args) | ||
} | ||
@@ -385,4 +330,3 @@ | ||
function poll () { | ||
framebufferState.poll() | ||
glState.poll() | ||
core.procs.poll() | ||
} | ||
@@ -436,2 +380,4 @@ | ||
core.procs.refresh() | ||
return extend(compileProcedure, { | ||
@@ -478,5 +424,7 @@ // Clear current FBO | ||
// Expose context attributes | ||
attributes: gl.getContextAttributes(), | ||
// Frame rendering | ||
frame: frame, | ||
stats: frameState, | ||
@@ -490,4 +438,10 @@ // System limits | ||
// Destroy regl and all associated resources | ||
destroy: destroy | ||
destroy: destroy, | ||
// Direct GL state manipulation | ||
_gl: gl, | ||
_refresh: function () { | ||
core.procs.refresh() | ||
} | ||
}) | ||
} |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
1814845
27
14138
225
80