Skip to content

Commit

Permalink
feat(shadertools): Add fp16 utils (#2056)
Browse files Browse the repository at this point in the history
  • Loading branch information
ibgreen committed Mar 29, 2024
1 parent e0edd3f commit 9ed3a8b
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 5 deletions.
7 changes: 4 additions & 3 deletions modules/shadertools/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,13 @@ export {getDependencyGraph as _getDependencyGraph} from './lib/shader-assembly/r
// EXPERIMENTAL WGSL
export {getShaderLayoutFromWGSL} from './lib/wgsl/get-shader-layout-wgsl';

// SHADER MODULES - WEBGL1 VERSION

// utils
// data utils
export {toHalfFloat, fromHalfFloat} from './modules/math/fp16/fp16-utils';
export {fp64ify, fp64LowPart, fp64ifyMatrix4} from './modules/math/fp64/fp64-utils';

// math libraries
export {random} from './modules/math/random/random';

export {fp32} from './modules/math/fp32/fp32';
// export {fp64, fp64arithmetic} from './modules/math/fp64/fp64';

Expand Down
2 changes: 1 addition & 1 deletion modules/shadertools/src/modules-webgl1/math/fp64/fp64.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

import {fp64ify, fp64LowPart, fp64ifyMatrix4} from './fp64-utils';
import {fp64ify, fp64LowPart, fp64ifyMatrix4} from '../../../modules/math/fp64/fp64-utils';

import {fp64arithmeticShader} from './fp64-arithmetic-glsl';
import {fp64functionShader} from './fp64-functions-glsl';
Expand Down
155 changes: 155 additions & 0 deletions modules/shadertools/src/modules/math/fp16/fp16-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// luma.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

// Forked from THREE.js under MIT license
// Fast Half Float Conversions, http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf

import {clamp} from '@math.gl/core';

/** Pre-calculated tables for float16 conversion */
type Float16Tables = {
// float32 to float16 helpers
baseTable: Uint32Array;
shiftTable: Uint32Array;
// float16 to float32 helpers
mantissaTable: Uint32Array;
exponentTable: Uint32Array;
offsetTable: Uint32Array;
};

/** */
let float16Tables: Float16Tables | null = null;

/** Storage that can be viewed both as float and integer */
const buffer = new ArrayBuffer(4);
const floatView = new Float32Array(buffer);
const uint32View = new Uint32Array(buffer);

/**
* float32 to float16
* @param val
* @returns
*/
export function toHalfFloat(val: number): number {
float16Tables ||= generateFloat16Tables();

// if ( Math.abs( val ) > 65504 ) console.warn( 'toHalfFloat(): Value out of range.' );

val = clamp(val, -65504, 65504);

floatView[0] = val;
const f = uint32View[0];
const e = (f >> 23) & 0x1ff;
return float16Tables.baseTable[e] + ((f & 0x007fffff) >> float16Tables.shiftTable[e]);
}

/**
* float16 to float32
* @param val
* @returns
*/
export function fromHalfFloat(val: number): number {
float16Tables ||= generateFloat16Tables();

const m = val >> 10;
uint32View[0] =
float16Tables.mantissaTable[float16Tables.offsetTable[m] + (val & 0x3ff)] +
float16Tables.exponentTable[m];
return floatView[0];
}

function generateFloat16Tables(): Float16Tables {
// float32 to float16 helpers

const baseTable = new Uint32Array(512);
const shiftTable = new Uint32Array(512);

for (let i = 0; i < 256; ++i) {
const e = i - 127;

// very small number (0, -0)

if (e < -27) {
baseTable[i] = 0x0000;
baseTable[i | 0x100] = 0x8000;
shiftTable[i] = 24;
shiftTable[i | 0x100] = 24;

// small number (denorm)
} else if (e < -14) {
baseTable[i] = 0x0400 >> (-e - 14);
baseTable[i | 0x100] = (0x0400 >> (-e - 14)) | 0x8000;
shiftTable[i] = -e - 1;
shiftTable[i | 0x100] = -e - 1;

// normal number
} else if (e <= 15) {
baseTable[i] = (e + 15) << 10;
baseTable[i | 0x100] = ((e + 15) << 10) | 0x8000;
shiftTable[i] = 13;
shiftTable[i | 0x100] = 13;

// large number (Infinity, -Infinity)
} else if (e < 128) {
baseTable[i] = 0x7c00;
baseTable[i | 0x100] = 0xfc00;
shiftTable[i] = 24;
shiftTable[i | 0x100] = 24;

// stay (NaN, Infinity, -Infinity)
} else {
baseTable[i] = 0x7c00;
baseTable[i | 0x100] = 0xfc00;
shiftTable[i] = 13;
shiftTable[i | 0x100] = 13;
}
}

// float16 to float32 helpers

const mantissaTable = new Uint32Array(2048);
const exponentTable = new Uint32Array(64);
const offsetTable = new Uint32Array(64);

for (let i = 1; i < 1024; ++i) {
let m = i << 13; // zero pad mantissa bits
let e = 0; // zero exponent

// normalized
while ((m & 0x00800000) === 0) {
m <<= 1;
e -= 0x00800000; // decrement exponent
}

m &= ~0x00800000; // clear leading 1 bit
e += 0x38800000; // adjust bias

mantissaTable[i] = m | e;
}

for (let i = 1024; i < 2048; ++i) {
mantissaTable[i] = 0x38000000 + ((i - 1024) << 13);
}

for (let i = 1; i < 31; ++i) {
exponentTable[i] = i << 23;
}

exponentTable[31] = 0x47800000;
exponentTable[32] = 0x80000000;

for (let i = 33; i < 63; ++i) {
exponentTable[i] = 0x80000000 + ((i - 32) << 23);
}

exponentTable[63] = 0xc7800000;

for (let i = 1; i < 64; ++i) {
if (i !== 32) {
offsetTable[i] = 1024;
}
}

return {baseTable, shiftTable, mantissaTable, exponentTable, offsetTable};
}
5 changes: 4 additions & 1 deletion modules/shadertools/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ import './lib/shader-assembler.spec';

// SHADER MODULE LIBRARY

// Data utilities
import './modules/math/fp16-utils.spec';
import './modules/math/fp64-utils.spec';

// General modules tests
import './modules/modules.spec';

Expand Down Expand Up @@ -72,7 +76,6 @@ import './modules/postprocessing/image-warp-filters/warp.spec';
// Math modules
// TODO - these are breaking in test-browser but not in test-headless??
import './modules-webgl1/fp64/fp64-arithmetic-transform.spec';
import './modules-webgl1/fp64/fp64-utils.spec';

// Light and picking
// import './modules-webgl1/dirlight/dirlight.spec';
Expand Down
37 changes: 37 additions & 0 deletions modules/shadertools/test/modules/math/fp16-utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// luma.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

// Forked from THREE.js under MIT license

import test from 'tape-promise/tape';
import {toHalfFloat, fromHalfFloat} from '@luma.gl/shadertools';

test('fp16#toHalfFloat', t => {
t.ok(toHalfFloat(0) === 0, 'Passed!');

// surpress the following console message during testing
// THREE.toHalfFloat(): Value out of range.

t.ok(toHalfFloat(100000) === 31743, 'Passed!');
t.ok(toHalfFloat(-100000) === 64511, 'Passed!');

t.ok(toHalfFloat(65504) === 31743, 'Passed!');
t.ok(toHalfFloat(-65504) === 64511, 'Passed!');
t.ok(toHalfFloat(Math.PI) === 16968, 'Passed!');
t.ok(toHalfFloat(-Math.PI) === 49736, 'Passed!');

t.end();
});

test('fp16#fromHalfFloat', t => {
t.ok(fromHalfFloat(0) === 0, 'Passed!');
t.ok(fromHalfFloat(31744) === Infinity, 'Passed!');
t.ok(fromHalfFloat(64512) === -Infinity, 'Passed!');
t.ok(fromHalfFloat(31743) === 65504, 'Passed!');
t.ok(fromHalfFloat(64511) === -65504, 'Passed!');
t.ok(fromHalfFloat(16968) === 3.140625, 'Passed!');
t.ok(fromHalfFloat(49736) === -3.140625, 'Passed!');

t.end();
});

0 comments on commit 9ed3a8b

Please sign in to comment.