Socket
Socket
Sign inDemoInstall

regl

Package Overview
Dependencies
Maintainers
1
Versions
42
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

regl - npm Package Compare versions

Comparing version 0.5.0 to 0.6.0

lib/core.js

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

54

CHANGES.md

@@ -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 @@

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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc