Skip to content

v2.1.0

Compare
Choose a tag to compare
@gre gre released this 08 Jan 17:48
· 510 commits to master since this release

This Release Notes applies for gl-react 2.1.0, gl-react-native 2.18.0, gl-react-dom 2.1.0.

Bugfix race conditions and improve performance

Since the last release notes, performance have been improved and some important bugs (like race conditions) has been fixed and released via a few patch versions. This will not be detailed here but you can find out more by browsing the commits if you are interested.

"real" react dependency

Hourray! Since react-native 0.18 depends on react, gl-react can now depends on react too. Now things are truly universal (did I already said that the last time? XD)

This is the good time to deprecate our old hacky way of injecting react / react-native. With this release, you will no more have to import/require() "gl-react/react" or "gl-react/react-native".

We have also deprecated GL.React, please directly import React from "react".

captureFrame(config)

captureFrame() now allows an optional config object in parameter.

: We have also removed the previously deprecated callback syntax. You must use the Promise returned by captureFrame().

With this config object you can choose among different file types (PNG, JPG, WEBM), different levels of quality (from 0% to 100%, which allows to compress more or less the file size) and different export formats (to base64, to Blob, save to file).

For more info, read the documentation.

Usage in gl-react-dom-static-container

You can also see captureFrame() in action in gl-react-dom-static-container demo.

gl-react-dom-static-container is a new module for gl-react-dom you might need if you want to display more than ~15 simultaneous <Surface> on the same page. It is a limitation in WebGL that you can't have so many context running at the same time, that library will snapshot the canvas with captureFrame() and cache it when it's not "active". gl-react-dom has also recently been improved to implement a "pool of canvas" to re-use WebGL context across <Surface> instances.

Inline Shader support!

Before this release, the only way to create shaders was with GL.Shaders.create, which statically define shaders and returns ids that can be used in <GL.Node shader={...}>.

You can now also pass-in the shader object inline to that shader props.

<GL.Node shader={{ frag: "...fragmentShaderCode..." }} />

onShaderCompile prop

You can also optionally hook to shader compilation state with a new onShaderCompile props: it receives 2 parameters error and result. error is a text with the raw GL Error message. If error is null the compilation was successful and result will be filled with { uniforms } where uniforms is an object describing the shader uniform types (retrieved after compiling the shader). This gets called after render(). If you don't provide a onShaderCompile callback, the default behavior is used: an error message will be logged in the console when it fails to compile.

Example

<GL.Node
  shader={{
    frag:`
precision highp float;
varying vec2 uv;
uniform float value;
void main () {
  gl_FragColor = vec4(uv.x, uv.y, value, 1.0);
}`
  }}
  uniforms={{ value: 0.5 }}
  onShaderCompile={(newError, result) => console.log("Shader Compile Response", newError, result)}
/>

It's pretty basic but it already allows to do things like a GLSL editor:

As shown in this GIF, gl-react reconciliates Shaders to minimize the add & remove events. Shaders also get factorized across Surface (if you define one shader twice, the same shader will be created and used and will be removed if all references are unmounted – if you pass-in inline). Finally, the remove event is getting debounced so if you toggle on/off a Surface fast enough, the algorithm won't create new shader but re-use previous shader (even if it was not anymore used).

Technically, we use reference counting (like in a Garbage Collector) to know if a shader is used or not, and the "remove" operation happens at appropriate time: when the app is not active (0.5s debounce) or if there is a lot of unused shaders (> 20 shaders, GC trigger with a 0.5s throttle).

These optimizations allow to not overload the CPU/GPU of shader compilation, it is especially important for the React Native implementation which have the latency of the JS<>ObjC bridge.

To summary, performance should be almost the same as using a static Shaders.create, it just depends if you want a temporary shader or not.

GL.Shaders.create(spec,onAllCompiled)

By default, GL.Shaders.create will console.error each shader that fails. If you want to override this and hook to shader compilation status, you can provide a second parameter to create that will be called once with 2 parameters: errors and results. errors is null if all shaders are successful or an object describing the errors per shader. results is an object containing the shader types information per shader.

pixelRatio prop

A new prop pixelRatio has been introduced both on <Surface> and GL.Node.
It allows to override the "devicePixelRatio" (aka screen scale) to use.
By default, window.devicePixelRatio (Web) or PixelRatio.get() (React Native) will be used.

This can be useful to not follow the device scale but strictly follow the desired resolution
(e.g if you do an effect over an image and want to keep the same dimension).

GL.Node will have a pixelRatio value inherited from the parent (in the effect tree).

GL.createComponent props

GL.createComponent takes a render function in parameter from props to a <GL.Node> element.

gl-react 1.1.0 introduced the fact that this props is the union of the inherited {width, height, pixelRatio} with user defined props.

This way, a GL Component can always assume to receive width, height and pixelRatio in parameter, either provided by the user or inherited from parent, and use them in your shader uniforms.

This was useful to remove the need to pass-in width and height when you need it, for instance, in gl-react-blur component.

This is also useful to provide a resolution parameter (a bunch of existing shaders you can find on the web use gl_FragCoord and a resolution uniform to resolve the coordinate).

Most of the time the user shouldn't have to worry about them anymore (except at the Surface top-level):

<Surface width={300} height={200}>
  <Blur factor={1}>
    <Negative>
      <video ... />
    </Negative>
  </Blur>
</Surface>

Improve uniform validation

Passing in weird values (like NaN) in uniform used to crash things (especially on React Native implementation) so there is now more check for these extreme cases.