Skip to content
This repository has been archived by the owner on Aug 15, 2019. It is now read-only.

Commit

Permalink
add experimental webgl support for RN platform (#1844)
Browse files Browse the repository at this point in the history
* fix tests add webgl backend prototype

* webgl path for ios

* save

* avoid shadowing global functions in clip

* upgrade tfjs version

* remove unused constant in shader compiler

* Merge branch 'master' into rn-webgl

* code review fixes

improve float texture download detection to include half float textures.

* save

* save

* remove warning as it makes tests noisy

consider refactoring this in another PR.

* Merge branch 'master' into rn-webgl

* revert settings update

* add comment

* save

* fix fetching of binary files

* save

* save

* have tests spy on platform.fetch

* Merge branch 'master' into rn-webgl

* code review fixes

* save

* Merge branch 'master' into rn-webgl

* fix lint errors

* code review fixes
  • Loading branch information
tafsiri authored Jul 29, 2019
1 parent 03c205e commit 95e44a5
Show file tree
Hide file tree
Showing 29 changed files with 505 additions and 251 deletions.
2 changes: 2 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ rollup.config.js
tsconfig.json
.yalc/
yalc.lock
tfjs-react-native/
tfjs-backend-nodegl/
10 changes: 5 additions & 5 deletions src/backends/webgl/clip_gpu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ export class ClipProgram implements GPGPUProgram {
constructor(aShape: number[]) {
this.outputShape = aShape;
this.userCode = `
uniform float min;
uniform float max;
uniform float minVal;
uniform float maxVal;
void main() {
float value = getAAtOutCoords();
Expand All @@ -40,16 +40,16 @@ export class ClipProgram implements GPGPUProgram {
return;
}
setOutput(clamp(value, min, max));
setOutput(clamp(value, minVal, maxVal));
}
`;
}

getCustomSetupFunc(min: number, max: number) {
return (gpgpu: GPGPUContext, webGLProgram: WebGLProgram) => {
if (this.minLoc == null) {
this.minLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'min');
this.maxLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'max');
this.minLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'minVal');
this.maxLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'maxVal');
}
gpgpu.gl.uniform1f(this.minLoc, min);
gpgpu.gl.uniform1f(this.maxLoc, max);
Expand Down
10 changes: 5 additions & 5 deletions src/backends/webgl/clip_packed_gpu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ export class ClipPackedProgram implements GPGPUProgram {
constructor(aShape: number[]) {
this.outputShape = aShape;
this.userCode = `
uniform float min;
uniform float max;
uniform float minVal;
uniform float maxVal;
void main() {
vec4 value = getAAtOutCoords();
Expand All @@ -42,16 +42,16 @@ export class ClipPackedProgram implements GPGPUProgram {
return;
}
setOutput(clamp(value, vec4(min), vec4(max)));
setOutput(clamp(value, vec4(minVal), vec4(maxVal)));
}
`;
}

getCustomSetupFunc(min: number, max: number) {
return (gpgpu: GPGPUContext, webGLProgram: WebGLProgram) => {
if (this.minLoc == null) {
this.minLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'min');
this.maxLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'max');
this.minLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'minVal');
this.maxLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'maxVal');
}
gpgpu.gl.uniform1f(this.minLoc, min);
gpgpu.gl.uniform1f(this.maxLoc, max);
Expand Down
6 changes: 3 additions & 3 deletions src/backends/webgl/glsl_version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ export function getGlslDifferences(): GLSL {
return (val > 0. || val < 0. || val == 0.) ? false : true;
}
`;
defineSpecialInf = `
const float INFINITY = uintBitsToFloat(uint(0x7f800000));
`;
// In webgl 2 we do not need to specify a custom isinf so there is no
// need for a special INFINITY constant.
defineSpecialInf = ``;
defineRound = `
#define round(value) newRound(value)
int newRound(float value) {
Expand Down
17 changes: 13 additions & 4 deletions src/backends/webgl/gpgpu_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import * as util from '../../util';

import {getWebGLContext, setWebGLContext} from './canvas_util';
import * as gpgpu_util from './gpgpu_util';
import {TextureConfig} from './gpgpu_util';
import * as tex_util from './tex_util';
import {TextureConfig} from './tex_util';
import {WebGL1DisjointQueryTimerExtension, WebGL2DisjointQueryTimerExtension} from './webgl_types';
import * as webgl_util from './webgl_util';

Expand Down Expand Up @@ -70,16 +70,25 @@ export class GPGPUContext {
this.gl.getExtension('EXT_color_buffer_half_float');
}
} else {
this.colorBufferFloatExtension = webgl_util.getExtensionOrThrow(
this.gl, this.debug, 'EXT_color_buffer_float');
const COLOR_BUFFER_FLOAT = 'EXT_color_buffer_float';
const COLOR_BUFFER_HALF_FLOAT = 'EXT_color_buffer_half_float';
if (webgl_util.hasExtension(this.gl, COLOR_BUFFER_FLOAT)) {
this.colorBufferFloatExtension =
this.gl.getExtension(COLOR_BUFFER_FLOAT);
} else if (webgl_util.hasExtension(this.gl, COLOR_BUFFER_HALF_FLOAT)) {
this.colorBufferHalfFloatExtension =
this.gl.getExtension(COLOR_BUFFER_HALF_FLOAT);
} else {
throw new Error('GL context does not support color renderable floats');
}
}

this.vertexBuffer = gpgpu_util.createVertexBuffer(this.gl, this.debug);
this.indexBuffer = gpgpu_util.createIndexBuffer(this.gl, this.debug);
this.framebuffer = webgl_util.createFramebuffer(this.gl, this.debug);

this.textureConfig =
gpgpu_util.getTextureConfig(this.gl, this.textureHalfFloatExtension);
tex_util.getTextureConfig(this.gl, this.textureHalfFloatExtension);
}

private get debug(): boolean {
Expand Down
72 changes: 1 addition & 71 deletions src/backends/webgl/gpgpu_util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,13 @@
* =============================================================================
*/

import {ENV} from '../../environment';
import {PixelData, TypedArray} from '../../types';

import {getGlslDifferences} from './glsl_version';
import * as tex_util from './tex_util';
import {TextureConfig} from './tex_util';
import * as webgl_util from './webgl_util';

export interface TextureConfig {
internalFormatFloat: number;
textureFormatFloat: number;
internalFormatPackedHalfFloat: number;
internalFormatHalfFloat: number;
internalFormatPackedFloat: number;

// The format to use during a gl.readPixels call.
downloadTextureFormat: number;
// How many channels need to be unpacked after a gl.readPixels call.
downloadUnpackNumChannels: number;

defaultNumChannels: number;
textureTypeHalfFloat: number;
}

export function createVertexShader(
gl: WebGLRenderingContext, debug: boolean): WebGLShader {
const glsl = getGlslDifferences();
Expand Down Expand Up @@ -69,60 +53,6 @@ export function createIndexBuffer(
return webgl_util.createStaticIndexBuffer(gl, debug, triangleVertexIndices);
}

export function getTextureConfig(
// tslint:disable-next-line:no-any
gl: WebGLRenderingContext, textureHalfFloatExtension?: any): TextureConfig {
// tslint:disable-next-line:no-any
const glany = gl as any;

let internalFormatFloat: number;
let internalFormatHalfFloat: number;
let internalFormatPackedHalfFloat: number;
let internalFormatPackedFloat: number;
let textureFormatFloat: number;

let downloadTextureFormat: number;
let downloadUnpackNumChannels: number;

let defaultNumChannels: number;
let textureTypeHalfFloat: number;

if (ENV.getNumber('WEBGL_VERSION') === 2) {
internalFormatFloat = glany.R32F;
internalFormatHalfFloat = glany.R16F;
internalFormatPackedHalfFloat = glany.RGBA16F;
internalFormatPackedFloat = glany.RGBA32F;
textureFormatFloat = glany.RED;
downloadUnpackNumChannels = 4;
defaultNumChannels = 1;
textureTypeHalfFloat = glany.HALF_FLOAT;
} else {
internalFormatFloat = gl.RGBA;
internalFormatHalfFloat = gl.RGBA;
internalFormatPackedHalfFloat = gl.RGBA;
internalFormatPackedFloat = glany.RGBA;
textureFormatFloat = gl.RGBA;
downloadUnpackNumChannels = 4;
defaultNumChannels = 4;
textureTypeHalfFloat = textureHalfFloatExtension != null ?
textureHalfFloatExtension.HALF_FLOAT_OES :
null;
}
downloadTextureFormat = gl.RGBA;

return {
internalFormatFloat,
internalFormatHalfFloat,
internalFormatPackedHalfFloat,
internalFormatPackedFloat,
textureFormatFloat,
downloadTextureFormat,
downloadUnpackNumChannels,
defaultNumChannels,
textureTypeHalfFloat
};
}

function createAndConfigureTexture(
gl: WebGLRenderingContext, debug: boolean, width: number, height: number,
internalFormat: number, textureFormat: number,
Expand Down
9 changes: 5 additions & 4 deletions src/backends/webgl/gpgpu_util_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {describeWithFlags} from '../../jasmine_util';
import {WEBGL_ENVS} from './backend_webgl_test_registry';
import {GPGPUContext} from './gpgpu_context';
import * as gpgpu_util from './gpgpu_util';
import * as tex_util from './tex_util';

describeWithFlags('gpgpu_util createWebGLContext', WEBGL_ENVS, () => {
let gpgpu: GPGPUContext;
Expand Down Expand Up @@ -59,7 +60,7 @@ describeWithFlags('gpgpu_util createWebGLContext', WEBGL_ENVS, () => {
describeWithFlags('gpgpu_util createFloat32MatrixTexture', WEBGL_ENVS, () => {
it('sets the TEXTURE_WRAP S+T parameters to CLAMP_TO_EDGE', () => {
const gpgpu = new GPGPUContext();
const textureConfig = gpgpu_util.getTextureConfig(gpgpu.gl);
const textureConfig = tex_util.getTextureConfig(gpgpu.gl);
const debug = false;
const tex = gpgpu_util.createFloat32MatrixTexture(
gpgpu.gl, debug, 32, 32, textureConfig);
Expand All @@ -77,7 +78,7 @@ describeWithFlags('gpgpu_util createFloat32MatrixTexture', WEBGL_ENVS, () => {

it('sets the TEXTURE_[MIN|MAG]_FILTER parameters to NEAREST', () => {
const gpgpu = new GPGPUContext();
const textureConfig = gpgpu_util.getTextureConfig(gpgpu.gl);
const textureConfig = tex_util.getTextureConfig(gpgpu.gl);
const debug = false;
const tex = gpgpu_util.createFloat32MatrixTexture(
gpgpu.gl, debug, 32, 32, textureConfig);
Expand All @@ -97,7 +98,7 @@ describeWithFlags('gpgpu_util createFloat32MatrixTexture', WEBGL_ENVS, () => {
describeWithFlags('gpgpu_util createPackedMatrixTexture', WEBGL_ENVS, () => {
it('sets the TEXTURE_WRAP S+T parameters to CLAMP_TO_EDGE', () => {
const gpgpu = new GPGPUContext();
const textureConfig = gpgpu_util.getTextureConfig(gpgpu.gl);
const textureConfig = tex_util.getTextureConfig(gpgpu.gl);
const debug = false;
const tex = gpgpu_util.createPackedMatrixTexture(
gpgpu.gl, debug, 32, 32, textureConfig);
Expand All @@ -115,7 +116,7 @@ describeWithFlags('gpgpu_util createPackedMatrixTexture', WEBGL_ENVS, () => {

it('sets the TEXTURE_[MIN|MAG]_FILTER parameters to NEAREST', () => {
const gpgpu = new GPGPUContext();
const textureConfig = gpgpu_util.getTextureConfig(gpgpu.gl);
const textureConfig = tex_util.getTextureConfig(gpgpu.gl);
const debug = false;
const tex = gpgpu_util.createPackedMatrixTexture(
gpgpu.gl, debug, 32, 32, textureConfig);
Expand Down
76 changes: 76 additions & 0 deletions src/backends/webgl/tex_util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* =============================================================================
*/

import {ENV} from '../../environment';
import {DataId, Tensor} from '../../tensor';
import {BackendValues, DataType} from '../../types';
import * as util from '../../util';
Expand Down Expand Up @@ -121,3 +122,78 @@ export function getPackedRGBAArraySizeFromMatrixShape(
const [w, h] = getPackedMatrixTextureShapeWidthHeight(rows, columns);
return w * h * 4;
}

export interface TextureConfig {
internalFormatFloat: number;
textureFormatFloat: number;
internalFormatPackedHalfFloat: number;
internalFormatHalfFloat: number;
internalFormatPackedFloat: number;

// The format to use during a gl.readPixels call.
downloadTextureFormat: number;
// How many channels need to be unpacked after a gl.readPixels call.
downloadUnpackNumChannels: number;

defaultNumChannels: number;
textureTypeHalfFloat: number;
textureTypeFloat: number;
}

export function getTextureConfig(
// tslint:disable-next-line:no-any
gl: WebGLRenderingContext, textureHalfFloatExtension?: any): TextureConfig {
// tslint:disable-next-line:no-any
const glany = gl as any;

let internalFormatFloat: number;
let internalFormatHalfFloat: number;
let internalFormatPackedHalfFloat: number;
let internalFormatPackedFloat: number;
let textureFormatFloat: number;

let downloadTextureFormat: number;
let downloadUnpackNumChannels: number;

let defaultNumChannels: number;
let textureTypeHalfFloat: number;
let textureTypeFloat: number;

if (ENV.getNumber('WEBGL_VERSION') === 2) {
internalFormatFloat = glany.R32F;
internalFormatHalfFloat = glany.R16F;
internalFormatPackedHalfFloat = glany.RGBA16F;
internalFormatPackedFloat = glany.RGBA32F;
textureFormatFloat = glany.RED;
downloadUnpackNumChannels = 4;
defaultNumChannels = 1;
textureTypeHalfFloat = glany.HALF_FLOAT;
textureTypeFloat = glany.FLOAT;
} else {
internalFormatFloat = gl.RGBA;
internalFormatHalfFloat = gl.RGBA;
internalFormatPackedHalfFloat = gl.RGBA;
internalFormatPackedFloat = glany.RGBA;
textureFormatFloat = gl.RGBA;
downloadUnpackNumChannels = 4;
defaultNumChannels = 4;
textureTypeHalfFloat = textureHalfFloatExtension != null ?
textureHalfFloatExtension.HALF_FLOAT_OES :
null;
textureTypeFloat = gl.FLOAT;
}
downloadTextureFormat = gl.RGBA;

return {
internalFormatFloat,
internalFormatHalfFloat,
internalFormatPackedHalfFloat,
internalFormatPackedFloat,
textureFormatFloat,
downloadTextureFormat,
downloadUnpackNumChannels,
defaultNumChannels,
textureTypeHalfFloat,
textureTypeFloat
};
}
Loading

0 comments on commit 95e44a5

Please sign in to comment.