From 3cdf5ff7bcf3d23abfd2595b053ea9109bea1e4d Mon Sep 17 00:00:00 2001 From: Ib Green Date: Mon, 1 Nov 2021 07:18:11 -0700 Subject: [PATCH] chore(webgl): use new shadertools function for shader log parsing (#1516) --- .../engine/src/geometries/cone-geometry.ts | 2 +- .../engine/src/transform/texture-transform.ts | 12 +- modules/engine/src/transform/transform.ts | 7 +- modules/webgl/src/classes/shader.ts | 44 +- .../src/glsl-utils/format-glsl-error.d.ts | 16 - .../webgl/src/glsl-utils/format-glsl-error.js | 107 -- .../webgl/src/glsl-utils/get-shader-name.ts | 7 - .../src/glsl-utils/get-shader-type-name.ts | 13 - .../src/glsl-utils/get-shader-version.ts | 12 - modules/webgl/src/glsl-utils/index.js | 8 - modules/webgl/src/index.ts | 9 +- .../webgl-utils/parse-shader-compiler-log.ts | 48 + .../test/glsl-utils/format-glsl-error.spec.js | 1101 ----------------- .../test/glsl-utils/get-shader-name.spec.js | 20 - .../glsl-utils/get-shader-version.spec.js | 40 - modules/webgl/test/glsl-utils/index.js | 3 - modules/webgl/test/index.js | 2 +- .../parse-shader-compiler-log.spec.js | 162 +++ 18 files changed, 248 insertions(+), 1365 deletions(-) delete mode 100644 modules/webgl/src/glsl-utils/format-glsl-error.d.ts delete mode 100644 modules/webgl/src/glsl-utils/format-glsl-error.js delete mode 100644 modules/webgl/src/glsl-utils/get-shader-name.ts delete mode 100644 modules/webgl/src/glsl-utils/get-shader-type-name.ts delete mode 100644 modules/webgl/src/glsl-utils/get-shader-version.ts delete mode 100644 modules/webgl/src/glsl-utils/index.js create mode 100644 modules/webgl/src/webgl-utils/parse-shader-compiler-log.ts delete mode 100644 modules/webgl/test/glsl-utils/format-glsl-error.spec.js delete mode 100644 modules/webgl/test/glsl-utils/get-shader-name.spec.js delete mode 100644 modules/webgl/test/glsl-utils/get-shader-version.spec.js delete mode 100644 modules/webgl/test/glsl-utils/index.js create mode 100644 modules/webgl/test/webgl-utils/parse-shader-compiler-log.spec.js diff --git a/modules/engine/src/geometries/cone-geometry.ts b/modules/engine/src/geometries/cone-geometry.ts index 35151ac66..e1f859215 100644 --- a/modules/engine/src/geometries/cone-geometry.ts +++ b/modules/engine/src/geometries/cone-geometry.ts @@ -5,7 +5,7 @@ export type ConeGeometryProps = { id?: string; radius?: number; cap?: boolean; -} +}; export class ConeGeometry extends TruncatedConeGeometry { constructor(props: ConeGeometryProps = {}) { diff --git a/modules/engine/src/transform/texture-transform.ts b/modules/engine/src/transform/texture-transform.ts index 772fe258a..3a877ee02 100644 --- a/modules/engine/src/transform/texture-transform.ts +++ b/modules/engine/src/transform/texture-transform.ts @@ -1,16 +1,10 @@ import GL from '@luma.gl/constants'; -import { - cloneTextureFrom, - readPixelsToArray, - getShaderVersion, - Buffer, - Texture2D, - Framebuffer -} from '@luma.gl/webgl'; +import {cloneTextureFrom, readPixelsToArray, Buffer, Texture2D, Framebuffer} from '@luma.gl/webgl'; import { _transform as transformModule, + getShaderInfo, getPassthroughFS, typeToChannelCount, combineInjects @@ -336,7 +330,7 @@ export default class TextureTransform { const fs = props._fs || getPassthroughFS({ - version: getShaderVersion(vs), + version: getShaderInfo(vs).version, input: this.targetTextureVarying, inputType: targetTextureType, output: FS_OUTPUT_VARIABLE diff --git a/modules/engine/src/transform/transform.ts b/modules/engine/src/transform/transform.ts index 9bc67e57f..3e6ad5081 100644 --- a/modules/engine/src/transform/transform.ts +++ b/modules/engine/src/transform/transform.ts @@ -1,8 +1,9 @@ import GL from '@luma.gl/constants'; -import {getPassthroughFS} from '@luma.gl/shadertools'; +import {getShaderInfo, getPassthroughFS} from '@luma.gl/shadertools'; import {isWebGL2} from '@luma.gl/gltools'; import type {Framebuffer, Buffer} from '@luma.gl/webgl'; -import {assert, isObjectEmpty, getShaderVersion} from '@luma.gl/webgl'; +import {assert, isObjectEmpty} from '@luma.gl/webgl'; + import Model from '../lib/model'; import BufferTransform from './buffer-transform'; import TextureTransform from './texture-transform'; @@ -110,7 +111,7 @@ export default class Transform { this.model = new Model( gl, Object.assign({}, props, { - fs: props.fs || getPassthroughFS({version: getShaderVersion(props.vs)}), + fs: props.fs || getPassthroughFS({version: getShaderInfo(props.vs).version}), id: props.id || 'transform-model', drawMode: props.drawMode || GL.POINTS, vertexCount: props.elementCount diff --git a/modules/webgl/src/classes/shader.ts b/modules/webgl/src/classes/shader.ts index f0f4c3ccf..58865509e 100644 --- a/modules/webgl/src/classes/shader.ts +++ b/modules/webgl/src/classes/shader.ts @@ -1,6 +1,8 @@ +// luma.gl, MIT license import GL from '@luma.gl/constants'; import {assertWebGLContext, log} from '@luma.gl/gltools'; -import {parseGLSLCompilerError, getShaderName} from '../glsl-utils'; +import {getShaderInfo, CompilerMessage, formatCompilerLog} from '@luma.gl/shadertools'; +import {parseShaderCompilerLog} from '../webgl-utils/parse-shader-compiler-log'; import {uid, assert} from '../utils'; import Resource, {ResourceProps} from './resource'; @@ -16,16 +18,21 @@ export type ShaderProps = ImmutableShaderProps & { const ERR_SOURCE = 'Shader: GLSL source code must be a JavaScript string'; export class ImmutableShader extends Resource { - private readonly _stage: 'vertex' | 'fragment'; + readonly stage: 'vertex' | 'fragment'; constructor(gl: WebGLRenderingContext, props: ShaderProps) { super(gl, {id: getShaderIdFromProps(props)}); - this._stage = props.stage; + this.stage = props.stage; this._compile(props.source); } - // PRIVATE METHODS - _compile(source) { + async compilationInfo(): Promise { + const log = this.gl.getShaderInfoLog(this.handle); + return parseShaderCompilerLog(log); + } + + // PRIVATE METHODS + _compile(source) { if (!source.startsWith('#version ')) { source = `#version 100\n${source}`; } @@ -37,22 +44,19 @@ export class ImmutableShader extends Resource { // https://gamedev.stackexchange.com/questions/30429/how-to-detect-glsl-warnings const compileStatus = this.getParameter(GL.COMPILE_STATUS); if (!compileStatus) { - const infoLog = this.gl.getShaderInfoLog(this.handle); - const {shaderName, errors, warnings} = parseGLSLCompilerError( - infoLog, - source, - this._stage === 'vertex' ? GL.VERTEX_SHADER : GL.FRAGMENT_SHADER, - this.id - ); - log.error(`GLSL compilation errors in ${shaderName}\n${errors}`)(); - log.warn(`GLSL compilation warnings in ${shaderName}\n${warnings}`)(); + const shaderLog = this.gl.getShaderInfoLog(this.handle); + const messages = parseShaderCompilerLog(shaderLog).filter(message => message.type === 'error'); + const formattedLog = formatCompilerLog(messages, source); + const shaderName: string = getShaderInfo(source).name; + const shaderDescription = `${this.stage} shader ${shaderName}`; + log.error(`GLSL compilation errors in ${shaderName}\n${formattedLog}`)(); throw new Error(`GLSL compilation errors in ${shaderName}`); } } // PRIVATE METHODS _createHandle() { - return this.gl.createShader(this._stage === 'vertex' ? GL.VERTEX_SHADER : GL.FRAGMENT_SHADER); + return this.gl.createShader(this.stage === 'vertex' ? GL.VERTEX_SHADER : GL.FRAGMENT_SHADER); } _deleteHandle(): void { @@ -64,7 +68,7 @@ export class ImmutableShader extends Resource { * Encapsulates the compiled or linked Shaders that execute portions of the WebGL Pipeline * For now this is an internal class */ - export class Shader extends ImmutableShader { +export class Shader extends ImmutableShader { shaderType: GL.FRAGMENT_SHADER | GL.VERTEX_SHADER; source: string; @@ -89,7 +93,7 @@ export class ImmutableShader extends Resource { this.shaderType = props.shaderType; this.source = props.source; - const shaderName = getShaderName(props.source, null); + const shaderName = getShaderInfo(props.source).name; if (shaderName) { this.id = uid(shaderName); } @@ -97,7 +101,7 @@ export class ImmutableShader extends Resource { initialize(props: ShaderProps): this { this._compile(props.source); - const shaderName = getShaderName(props.source, null); + const shaderName = getShaderInfo(props.source).name; if (shaderName) { this.id = uid(shaderName); } @@ -115,7 +119,7 @@ export class ImmutableShader extends Resource { } getName(): string { - return getShaderName(this.source) || 'unnamed-shader'; + return getShaderInfo(this.source).name || 'unnamed-shader'; } getSource(): string { @@ -179,7 +183,7 @@ function getShaderProps(props: ShaderProps | string, shaderType: GL.VERTEX_SHADE /** Deduce an id, from shader source, or supplied id, or shader type */ function getShaderIdFromProps(props: ShaderProps): string { - return getShaderName(props.source, null) || + return getShaderInfo(props.source).name || props.id || uid(`unnamed ${props.stage || Shader.getTypeName(props.shaderType)}`); } diff --git a/modules/webgl/src/glsl-utils/format-glsl-error.d.ts b/modules/webgl/src/glsl-utils/format-glsl-error.d.ts deleted file mode 100644 index effd6cb85..000000000 --- a/modules/webgl/src/glsl-utils/format-glsl-error.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -export default function formatGLSLCompilerError(errLog: any, src: any, shaderType: any): string; - -/** - * Parse a GLSL compiler error log into a string showing the source code around each error. - * Based on https://github.com/wwwtyro/gl-format-compiler-error (public domain) - */ -export function parseGLSLCompilerError( - errLog: any, - src: any, - shaderType: any, - shaderName: any -): { - shaderName: string; - errors: string; - warnings: string; -}; diff --git a/modules/webgl/src/glsl-utils/format-glsl-error.js b/modules/webgl/src/glsl-utils/format-glsl-error.js deleted file mode 100644 index 9f4ce9456..000000000 --- a/modules/webgl/src/glsl-utils/format-glsl-error.js +++ /dev/null @@ -1,107 +0,0 @@ -// TODO - formatGLSLCompilerError should not depend on this -import getShaderName from './get-shader-name'; -import getShaderTypeName from './get-shader-type-name'; - -// Formats GLSL compiler error log into single string -export default function formatGLSLCompilerError(errLog, src, shaderType) { - const {shaderName, errors, warnings} = parseGLSLCompilerError(errLog, src, shaderType); - return `GLSL compilation error in ${shaderName}\n\n${errors}\n${warnings}`; -} - -/** - * Parse a GLSL compiler error log into a string showing the source code around each error. - * Based on https://github.com/wwwtyro/gl-format-compiler-error (public domain) - */ -/* eslint-disable no-continue, max-statements */ -export function parseGLSLCompilerError(errLog, src, shaderType, shaderName) { - const errorStrings = errLog.split(/\r?\n/); - const errors = {}; - const warnings = {}; - - // Patch the shader name - const name = shaderName || getShaderName(src) || '(unnamed)'; - const shaderDescription = `${getShaderTypeName(shaderType)} shader ${name}`; - - // Parse the error - note: browser and driver dependent - for (let i = 0; i < errorStrings.length; i++) { - const errorString = errorStrings[i]; - if (errorString.length <= 1) { - continue; - } - const segments = errorString.split(':'); - const type = segments[0]; - const line = parseInt(segments[2], 10); - if (isNaN(line)) { - throw new Error(`GLSL compilation error in ${shaderDescription}: ${errLog}`); - } - if (type !== 'WARNING') { - errors[line] = errorString; - } else { - warnings[line] = errorString; - } - } - - // Format the error inline with the code - const lines = addLineNumbers(src); - - return { - shaderName: shaderDescription, - errors: formatErrors(errors, lines), - warnings: formatErrors(warnings, lines) - }; -} - -// helper function, outputs annotated errors or warnings -function formatErrors(errors, lines) { - let message = ''; - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - if (!errors[i + 3] && !errors[i + 2] && !errors[i + 1]) { - continue; - } - message += `${line}\n`; - if (errors[i + 1]) { - const error = errors[i + 1]; - const segments = error.split(':', 3); - const type = segments[0]; - const column = parseInt(segments[1], 10) || 0; - const err = error.substring(segments.join(':').length + 1).trim(); - message += padLeft(`^^^ ${type}: ${err}\n\n`, column); - } - } - return message; -} - -/** - * Prepends line numbers to each line of a string. - * The line numbers will be left-padded with spaces to ensure an - * aligned layout when rendered using monospace fonts. - * @param {String} string - multi-line string to add line numbers to - * @param {Number} start=1 - number of spaces to add - * @param {String} delim =': ' - injected between line number and original line - * @return {String[]} strings - array of string, one per line, with line numbers added - */ -function addLineNumbers(string, start = 1, delim = ': ') { - const lines = string.split(/\r?\n/); - const maxDigits = String(lines.length + start - 1).length; - return lines.map((line, i) => { - const lineNumber = String(i + start); - const digits = lineNumber.length; - const prefix = padLeft(lineNumber, maxDigits - digits); - return prefix + delim + line; - }); -} - -/** - * Pads a string with a number of spaces (space characters) to the left - * @param {String} string - string to pad - * @param {Number} digits - number of spaces to add - * @return {String} string - The padded string - */ -function padLeft(string, digits) { - let result = ''; - for (let i = 0; i < digits; ++i) { - result += ' '; - } - return `${result}${string}`; -} diff --git a/modules/webgl/src/glsl-utils/get-shader-name.ts b/modules/webgl/src/glsl-utils/get-shader-name.ts deleted file mode 100644 index e43e82a3c..000000000 --- a/modules/webgl/src/glsl-utils/get-shader-name.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Supports GLSLIFY style naming of shaders -// #define SHADER_NAME ... -export default function getShaderName(shader, defaultName = 'unnamed') { - const SHADER_NAME_REGEXP = /#define[\s*]SHADER_NAME[\s*]([A-Za-z0-9_-]+)[\s*]/; - const match = shader.match(SHADER_NAME_REGEXP); - return match ? match[1] : defaultName; -} diff --git a/modules/webgl/src/glsl-utils/get-shader-type-name.ts b/modules/webgl/src/glsl-utils/get-shader-type-name.ts deleted file mode 100644 index e76263e07..000000000 --- a/modules/webgl/src/glsl-utils/get-shader-type-name.ts +++ /dev/null @@ -1,13 +0,0 @@ -const GL_FRAGMENT_SHADER = 0x8b30; -const GL_VERTEX_SHADER = 0x8b31; - -export default function getShaderTypeName(type) { - switch (type) { - case GL_FRAGMENT_SHADER: - return 'fragment'; - case GL_VERTEX_SHADER: - return 'vertex'; - default: - return 'unknown type'; - } -} diff --git a/modules/webgl/src/glsl-utils/get-shader-version.ts b/modules/webgl/src/glsl-utils/get-shader-version.ts deleted file mode 100644 index 07f25df83..000000000 --- a/modules/webgl/src/glsl-utils/get-shader-version.ts +++ /dev/null @@ -1,12 +0,0 @@ -// returns GLSL shader version of given shader string -export default function getShaderVersion(source) { - let version = 100; - const words = source.match(/[^\s]+/g); - if (words.length >= 2 && words[0] === '#version') { - const v = parseInt(words[1], 10); - if (Number.isFinite(v)) { - version = v; - } - } - return version; -} diff --git a/modules/webgl/src/glsl-utils/index.js b/modules/webgl/src/glsl-utils/index.js deleted file mode 100644 index 1236511f8..000000000 --- a/modules/webgl/src/glsl-utils/index.js +++ /dev/null @@ -1,8 +0,0 @@ -// PARSE SHADER ERRORS -export {default as formatGLSLCompilerError, parseGLSLCompilerError} from './format-glsl-error'; - -// PARSE SHADER SOURCE -export {default as getShaderName} from './get-shader-name'; -export {default as getShaderVersion} from './get-shader-version'; - -export {default as getShaderTypeName} from './get-shader-type-name'; diff --git a/modules/webgl/src/index.ts b/modules/webgl/src/index.ts index 3f893e657..f28a56920 100644 --- a/modules/webgl/src/index.ts +++ b/modules/webgl/src/index.ts @@ -55,10 +55,6 @@ export {default as UniformBufferLayout} from './classes/uniform-buffer-layout'; export {setPathPrefix, loadFile, loadImage} from './utils/load-file'; -// PARSE SHADER SOURCE -export {default as getShaderName} from './glsl-utils/get-shader-name'; -export {default as getShaderVersion} from './glsl-utils/get-shader-version'; - // UTILS export {log} from '@luma.gl/gltools'; export {default as assert} from './utils/assert'; @@ -69,3 +65,8 @@ export {parseUniformName, getUniformSetter} from './classes/uniforms'; export {getDebugTableForUniforms} from './debug/debug-uniforms'; export {getDebugTableForVertexArray} from './debug/debug-vertex-array'; export {getDebugTableForProgramConfiguration} from './debug/debug-program-configuration'; + +// REMOVED in v8.6 +// getShaderInfo, +// getShaderName +// getShaderVersion diff --git a/modules/webgl/src/webgl-utils/parse-shader-compiler-log.ts b/modules/webgl/src/webgl-utils/parse-shader-compiler-log.ts new file mode 100644 index 000000000..fff4e9536 --- /dev/null +++ b/modules/webgl/src/webgl-utils/parse-shader-compiler-log.ts @@ -0,0 +1,48 @@ +// luma.gl, MIT license +import type {CompilerMessage} from '@luma.gl/shadertools'; + +const MESSAGE_TYPES = ['warning', 'error', 'info']; + +/** + * Parse a WebGL-format GLSL compilation log into an array of WebGPU style message records. + * This follows documented WebGL conventions for compilation logs. + * Based on https://github.com/wwwtyro/gl-format-compiler-error (public domain) + */ +export function parseShaderCompilerLog(errLog: string) : readonly CompilerMessage[] { + // Parse the error - note: browser and driver dependent + const lines = errLog.split(/\r?\n/); + + const messages: CompilerMessage[] = []; + + for (const line of lines) { + if (line.length <= 1) { + continue; + } + + const segments: string[] = line.split(':'); + const [messageType, linePosition, lineNumber, ...rest] = segments; + + let lineNum = parseInt(lineNumber, 10); + if (isNaN(lineNum)) { + lineNum = 0; + } + + let linePos = parseInt(linePosition, 10); + if (isNaN(linePos)) { + linePos = 0; + } + + // Ensure supported type + const lowerCaseType = messageType.toLowerCase(); + const type = (MESSAGE_TYPES.includes(lowerCaseType) ? lowerCaseType : 'info') as 'warning' | 'error' | 'info'; + + messages.push({ + message: rest.join(':').trim(), + type, + lineNum, + linePos // TODO + }) + } + + return messages; +} diff --git a/modules/webgl/test/glsl-utils/format-glsl-error.spec.js b/modules/webgl/test/glsl-utils/format-glsl-error.spec.js deleted file mode 100644 index 9037a6a7d..000000000 --- a/modules/webgl/test/glsl-utils/format-glsl-error.spec.js +++ /dev/null @@ -1,1101 +0,0 @@ -import formatGLSLError from '@luma.gl/webgl/glsl-utils/format-glsl-error'; -import test from 'tape-promise/tape'; - -const SHADER_SOURCE = ` -#define AMD_GPU - -// Defines for shader portability -#if (__VERSION__ > 120) -# define attribute in -# define varying out -#endif // __VERSION - -// FRAG_DEPTH => gl_FragDepth is available -#ifdef GL_EXT_frag_depth -#extension GL_EXT_frag_depth : enable -# define FRAG_DEPTH -# define gl_FragDepth gl_FragDepthEXT -#endif -#if (__VERSION__ > 120) -# define FRAG_DEPTH -#endif - -// DERIVATIVES => dxdF, dxdY and fwidth are available -#ifdef GL_OES_standard_derivatives -#extension GL_OES_standard_derivatives : enable -# define DERIVATIVES -#endif -#if (__VERSION__ > 120) -# define DERIVATIVES -#endif - -// DRAW_BUFFERS => gl_FragData[] is available -#ifdef GL_EXT_draw_buffers -#extension GL_EXT_draw_buffers : require -#define DRAW_BUFFERS -#endif -#if (__VERSION__ > 120) -# define DRAW_BUFFERS -#endif - -// TEXTURE_LOD => texture2DLod etc are available -#ifdef GL_EXT_shader_texture_lod -#extension GL_EXT_shader_texture_lod : enable -# define TEXTURE_LOD -#define texture2DLod texture2DLodEXT -#define texture2DProjLod texture2DProjLodEXT -#define texture2DProjLod texture2DProjLodEXT -#define textureCubeLod textureCubeLodEXT -#define texture2DGrad texture2DGradEXT -#define texture2DProjGrad texture2DProjGradEXT -#define texture2DProjGrad texture2DProjGradEXT -#define textureCubeGrad textureCubeGradEXT -#endif -#if (__VERSION__ > 120) -# define TEXTURE_LOD -#endif - - - - -#define MODULE_FP64 -uniform float ONE; - -const vec2 E_FP64 = vec2(2.7182817459106445e+00, 8.254840366817007e-08); -const vec2 LOG2_FP64 = vec2(0.6931471824645996e+00, -1.9046542121259336e-09); -const vec2 PI_FP64 = vec2(3.1415927410125732, -8.742278012618954e-8); -const vec2 TWO_PI_FP64 = vec2(6.2831854820251465, -1.7484556025237907e-7); -const vec2 PI_2_FP64 = vec2(1.5707963705062866, -4.371139006309477e-8); -const vec2 PI_4_FP64 = vec2(0.7853981852531433, -2.1855695031547384e-8); -const vec2 PI_16_FP64 = vec2(0.19634954631328583, -5.463923757886846e-9); -const vec2 PI_16_2_FP64 = vec2(0.39269909262657166, -1.0927847515773692e-8); -const vec2 PI_16_3_FP64 = vec2(0.5890486240386963, -1.4906100798128818e-9); -const vec2 PI_180_FP64 = vec2(0.01745329238474369, 1.3519960498364902e-10); - -const vec2 SIN_TABLE_0_FP64 = vec2(0.19509032368659973, -1.6704714833615242e-9); -const vec2 SIN_TABLE_1_FP64 = vec2(0.3826834261417389, 6.22335089017767e-9); -const vec2 SIN_TABLE_2_FP64 = vec2(0.5555702447891235, -1.1769521357507529e-8); -const vec2 SIN_TABLE_3_FP64 = vec2(0.7071067690849304, 1.2101617041793133e-8); - -const vec2 COS_TABLE_0_FP64 = vec2(0.9807852506637573, 2.9739473106360492e-8); -const vec2 COS_TABLE_1_FP64 = vec2(0.9238795042037964, 2.8307490351764386e-8); -const vec2 COS_TABLE_2_FP64 = vec2(0.8314695954322815, 1.6870263741530778e-8); -const vec2 COS_TABLE_3_FP64 = vec2(0.7071067690849304, 1.2101617152815436e-8); - -const vec2 INVERSE_FACTORIAL_3_FP64 = vec2(1.666666716337204e-01, -4.967053879312289e-09); // 1/3! -const vec2 INVERSE_FACTORIAL_4_FP64 = vec2(4.16666679084301e-02, -1.2417634698280722e-09); // 1/4! -const vec2 INVERSE_FACTORIAL_5_FP64 = vec2(8.333333767950535e-03, -4.34617203337595e-10); // 1/5! -const vec2 INVERSE_FACTORIAL_6_FP64 = vec2(1.3888889225199819e-03, -3.3631094437103215e-11); // 1/6! -const vec2 INVERSE_FACTORIAL_7_FP64 = vec2(1.9841270113829523e-04, -2.725596874933456e-12); // 1/7! -const vec2 INVERSE_FACTORIAL_8_FP64 = vec2(2.4801587642286904e-05, -3.406996025904184e-13); // 1/8! -const vec2 INVERSE_FACTORIAL_9_FP64 = vec2(2.75573188446287533e-06, 3.7935713937038186e-14); // 1/9! -const vec2 INVERSE_FACTORIAL_10_FP64 = vec2(2.755731998149713e-07, -7.575112367869873e-15); // 1/10! - -float nint(float d) { - if (d == floor(d)) return d; - return floor(d + 0.5); -} - -#if defined(NVIDIA_FP64_WORKAROUND) || defined(INTEL_FP64_WORKAROUND) -vec2 split(float a) { - const float SPLIT = 4097.0; - float t = a * SPLIT; - float a_hi = t * ONE - (t - a); - float a_lo = a * ONE - a_hi; - return vec2(a_hi, a_lo); -} -#else -vec2 split(float a) { - const float SPLIT = 4097.0; - float t = a * SPLIT; - float a_hi = t - (t - a); - float a_lo = a - a_hi; - return vec2(a_hi, a_lo); -} -#endif - -#if defined(NVIDIA_FP64_WORKAROUND) || defined(INTEL_FP64_WORKAROUND) -vec2 quickTwoSum(float a, float b) { - float sum = (a + b) * ONE; - float err = b - (sum - a) * ONE; - return vec2(sum, err); -} -#else -vec2 quickTwoSum(float a, float b) { - float sum = a + b; - float err = b - (sum - a); - return vec2(sum, err); -} -#endif - -vec2 nint_fp64(vec2 a) { - float hi = nint(a.x); - float lo; - vec2 tmp; - if (hi == a.x) { - lo = nint(a.y); - tmp = quickTwoSum(hi, lo); - } else { - lo = 0.0; - if (abs(hi - a.x) == 0.5 && a.y < 0.0) { - hi -= 1.0; - } - tmp = vec2(hi, lo); - } - return tmp; -} - -#if defined(NVIDIA_FP64_WORKAROUND) || defined(INTEL_FP64_WORKAROUND) - -/* The purpose of this workaround is to prevent compilers from -optimizing away necessary arithmetic operations by swapping their sequences -or transform the equation to some 'equivalent' from. - -The method is to multiply an artifical variable, ONE, which will be known to -the compiler to be one only at the runtime. The whole expression is then represented -as a polynomial with respective to ONE. In the coefficients of all terms, only one a -and one b should appear - -err = (a + b) * ONE^6 - a * ONE^5 - (a + b) * ONE^4 + a * ONE^3 - b - (a + b) * ONE^2 + a * ONE -*/ - -vec2 twoSum(float a, float b) { - float s = (a + b); - float v = (s * ONE - a) * ONE; - float err = (a - (s - v) * ONE) * ONE * ONE * ONE + (b - v); - return vec2(s, err); -} -#else -vec2 twoSum(float a, float b) { - float s = a + b; - float v = s - a; - float err = (a - (s - v)) + (b - v); - return vec2(s, err); -} -#endif - -#if defined(NVIDIA_FP64_WORKAROUND) || defined(INTEL_FP64_WORKAROUND) -/* Same thing as in twoSum() */ -vec2 twoSub(float a, float b) { - float s = (a - b); - float v = (s * ONE - a) * ONE; - float err = (a - (s - v) * ONE) * ONE * ONE * ONE - (b + v); - return vec2(s, err); -} -#else -vec2 twoSub(float a, float b) { - float s = a - b; - float v = s - a; - float err = (a - (s - v)) - (b + v); - return vec2(s, err); -} -#endif - -vec2 twoProd(float a, float b) { - float prod = a * b; - vec2 a_fp64 = split(a); - vec2 b_fp64 = split(b); - float err = ((a_fp64.x * b_fp64.x - prod) + a_fp64.x * b_fp64.y + - a_fp64.y * b_fp64.x) + a_fp64.y * b_fp64.y; - return vec2(prod, err); -} - -#if defined(NVIDIA_FP64_WORKAROUND) || defined(INTEL_FP64_WORKAROUND) -vec2 twoSqr(float a) { - float prod = a * a; - vec2 a_fp64 = split(a); - - float err = ((a_fp64.x * a_fp64.x - prod) * ONE + 2.0 * a_fp64.x * - a_fp64.y * ONE * ONE) + a_fp64.y * a_fp64.y * ONE * ONE * ONE; - return vec2(prod, err); -} -#else -vec2 twoSqr(float a) { - float prod = a * a; - vec2 a_fp64 = split(a); - - float err = ((a_fp64.x * a_fp64.x - prod) + 2.0 * a_fp64.x * a_fp64.y) + a_fp64.y * a_fp64.y; - return vec2(prod, err); -} -#endif - -vec2 sum_fp64(vec2 a, vec2 b) { - vec2 s, t; - s = twoSum(a.x, b.x); - t = twoSum(a.y, b.y); - s.y += t.x; - s = quickTwoSum(s.x, s.y); - s.y += t.y; - s = quickTwoSum(s.x, s.y); - return s; -} - -vec2 sub_fp64(vec2 a, vec2 b) { - vec2 s, t; - s = twoSub(a.x, b.x); - t = twoSub(a.y, b.y); - s.y += t.x; - s = quickTwoSum(s.x, s.y); - s.y += t.y; - s = quickTwoSum(s.x, s.y); - return s; -} - -vec2 mul_fp64(vec2 a, vec2 b) { - vec2 prod = twoProd(a.x, b.x); - // y component is for the error - prod.y += a.x * b.y; - prod.y += a.y * b.x; - prod = quickTwoSum(prod.x, prod.y); - return prod; -} - -vec2 div_fp64(vec2 a, vec2 b) { - float xn = 1.0 / b.x; - vec2 yn = a * xn; - float diff = (sub_fp64(a, mul_fp64(b, yn))).x; - vec2 prod = twoProd(xn, diff); - return sum_fp64(yn, prod); -} - -vec2 sqrt_fp64(vec2 a) { - - if (a.x == 0.0 && a.y == 0.0) return vec2(0.0, 0.0); - if (a.x < 0.0) return vec2(0.0 / 0.0, 0.0 / 0.0); - - float x = 1.0 / sqrt(a.x); - float yn = a.x * x; -#if defined(NVIDIA_FP64_WORKAROUND) || defined(INTEL_FP64_WORKAROUND) - vec2 yn_sqr = twoSqr(yn) * ONE; -#else - vec2 yn_sqr = twoSqr(yn); -#endif - float diff = sub_fp64(a, yn_sqr).x; - vec2 prod = twoProd(x * 0.5, diff); - return sum_fp64(vec2(yn, 0.0), prod); -} - -/* k_power controls how much range reduction we would like to have -Range reduction uses the following method: -assume a = k_power * r + m * log(2), k and m being integers. -Set k_power = 4 (we can choose other k to trade accuracy with performance. -we only need to calculate exp(r) and using exp(a) = 2^m * exp(r)^k_power; -*/ - -vec2 exp_fp64(vec2 a) { - // We need to make sure these two numbers match - // as bit-wise shift is not available in GLSL 1.0 - const int k_power = 4; - const float k = 16.0; - - const float inv_k = 1.0 / k; - - if (a.x <= -88.0) return vec2(0.0, 0.0); - if (a.x >= 88.0) return vec2(1.0 / 0.0, 1.0 / 0.0); - if (a.x == 0.0 && a.y == 0.0) return vec2(1.0, 0.0); - if (a.x == 1.0 && a.y == 0.0) return E_FP64; - - float m = floor(a.x / LOG2_FP64.x + 0.5); - vec2 r = sub_fp64(a, mul_fp64(LOG2_FP64, vec2(m, 0.0))) * inv_k; - vec2 s, t, p; - - p = mul_fp64(r, r); - s = sum_fp64(r, p * 0.5); - p = mul_fp64(p, r); - t = mul_fp64(p, INVERSE_FACTORIAL_3_FP64); - - s = sum_fp64(s, t); - p = mul_fp64(p, r); - t = mul_fp64(p, INVERSE_FACTORIAL_4_FP64); - - s = sum_fp64(s, t); - p = mul_fp64(p, r); - t = mul_fp64(p, INVERSE_FACTORIAL_5_FP64); - - // s = sum_fp64(s, t); - // p = mul_fp64(p, r); - // t = mul_fp64(p, INVERSE_FACTORIAL_6_FP64); - - // s = sum_fp64(s, t); - // p = mul_fp64(p, r); - // t = mul_fp64(p, INVERSE_FACTORIAL_7_FP64); - - s = sum_fp64(s, t); - - - // At this point, s = exp(r) - 1; but after following 4 recursions, we will get exp(r) ^ 512 - 1. - for (int i = 0; i < k_power; i++) { - s = sum_fp64(s * 2.0, mul_fp64(s, s)); - } - -#if defined(NVIDIA_FP64_WORKAROUND) || defined(INTEL_FP64_WORKAROUND) - s = sum_fp64(s, vec2(ONE, 0.0)); -#else - s = sum_fp64(s, vec2(1.0, 0.0)); -#endif - - return s * pow(2.0, m); -// return r; -} - -vec2 log_fp64(vec2 a) -{ - if (a.x == 1.0 && a.y == 0.0) return vec2(0.0, 0.0); - if (a.x <= 0.0) return vec2(0.0 / 0.0, 0.0 / 0.0); - vec2 x = vec2(log(a.x), 0.0); - vec2 s; -#if defined(NVIDIA_FP64_WORKAROUND) || defined(INTEL_FP64_WORKAROUND) - s = vec2(ONE, 0.0); -#else - s = vec2(1.0, 0.0); -#endif - - x = sub_fp64(sum_fp64(x, mul_fp64(a, exp_fp64(-x))), s); - return x; -} - -vec2 sin_taylor_fp64(vec2 a) { - vec2 r, s, t, x; - - if (a.x == 0.0 && a.y == 0.0) { - return vec2(0.0, 0.0); - } - - x = -mul_fp64(a, a); - s = a; - r = a; - - r = mul_fp64(r, x); - t = mul_fp64(r, INVERSE_FACTORIAL_3_FP64); - s = sum_fp64(s, t); - - r = mul_fp64(r, x); - t = mul_fp64(r, INVERSE_FACTORIAL_5_FP64); - s = sum_fp64(s, t); - - /* keep the following commented code in case we need them - for extra accuracy from the Taylor expansion*/ - - // r = mul_fp64(r, x); - // t = mul_fp64(r, INVERSE_FACTORIAL_7_FP64); - // s = sum_fp64(s, t); - - // r = mul_fp64(r, x); - // t = mul_fp64(r, INVERSE_FACTORIAL_9_FP64); - // s = sum_fp64(s, t); - - return s; -} - -vec2 cos_taylor_fp64(vec2 a) { - vec2 r, s, t, x; - - if (a.x == 0.0 && a.y == 0.0) { - return vec2(1.0, 0.0); - } - - x = -mul_fp64(a, a); - r = x; - s = sum_fp64(vec2(1.0, 0.0), r * 0.5); - - r = mul_fp64(r, x); - t = mul_fp64(r, INVERSE_FACTORIAL_4_FP64); - s = sum_fp64(s, t); - - r = mul_fp64(r, x); - t = mul_fp64(r, INVERSE_FACTORIAL_6_FP64); - s = sum_fp64(s, t); - - /* keep the following commented code in case we need them - for extra accuracy from the Taylor expansion*/ - - // r = mul_fp64(r, x); - // t = mul_fp64(r, INVERSE_FACTORIAL_8_FP64); - // s = sum_fp64(s, t); - - // r = mul_fp64(r, x); - // t = mul_fp64(r, INVERSE_FACTORIAL_10_FP64); - // s = sum_fp64(s, t); - - return s; -} - -void sincos_taylor_fp64(vec2 a, out vec2 sin_t, out vec2 cos_t) { - if (a.x == 0.0 && a.y == 0.0) { - sin_t = vec2(0.0, 0.0); - cos_t = vec2(1.0, 0.0); - } - - sin_t = sin_taylor_fp64(a); - cos_t = sqrt_fp64(sub_fp64(vec2(1.0, 0.0), mul_fp64(sin_t, sin_t))); -} - -vec2 sin_fp64(vec2 a) { - if (a.x == 0.0 && a.y == 0.0) { - return vec2(0.0, 0.0); - } - - // 2pi range reduction - vec2 z = nint_fp64(div_fp64(a, TWO_PI_FP64)); - vec2 r = sub_fp64(a, mul_fp64(TWO_PI_FP64, z)); - - vec2 t; - float q = floor(r.x / PI_2_FP64.x + 0.5); - int j = int(q); - - if (j < -2 || j > 2) { - return vec2(0.0 / 0.0, 0.0 / 0.0); - } - - t = sub_fp64(r, mul_fp64(PI_2_FP64, vec2(q, 0.0))); - - q = floor(t.x / PI_16_FP64.x + 0.5); - int k = int(q); - - if (k == 0) { - if (j == 0) { - return sin_taylor_fp64(t); - } else if (j == 1) { - return cos_taylor_fp64(t); - } else if (j == -1) { - return -cos_taylor_fp64(t); - } else { - return -sin_taylor_fp64(t); - } - } - - int abs_k = int(abs(float(k))); - - if (abs_k > 4) { - return vec2(0.0 / 0.0, 0.0 / 0.0); - } else { - t = sub_fp64(t, mul_fp64(PI_16_FP64, vec2(q, 0.0))); - } - - vec2 u = vec2(0.0, 0.0); - vec2 v = vec2(0.0, 0.0); - -#if defined(NVIDIA_FP64_WORKAROUND) || defined(INTEL_FP64_WORKAROUND) - if (abs(float(abs_k) - 1.0) < 0.5) { - u = COS_TABLE_0_FP64; - v = SIN_TABLE_0_FP64; - } else if (abs(float(abs_k) - 2.0) < 0.5) { - u = COS_TABLE_1_FP64; - v = SIN_TABLE_1_FP64; - } else if (abs(float(abs_k) - 3.0) < 0.5) { - u = COS_TABLE_2_FP64; - v = SIN_TABLE_2_FP64; - } else if (abs(float(abs_k) - 4.0) < 0.5) { - u = COS_TABLE_3_FP64; - v = SIN_TABLE_3_FP64; - } -#else - if (abs_k == 1) { - u = COS_TABLE_0_FP64; - v = SIN_TABLE_0_FP64; - } else if (abs_k == 2) { - u = COS_TABLE_1_FP64; - v = SIN_TABLE_1_FP64; - } else if (abs_k == 3) { - u = COS_TABLE_2_FP64; - v = SIN_TABLE_2_FP64; - } else if (abs_k == 4) { - u = COS_TABLE_3_FP64; - v = SIN_TABLE_3_FP64; - } -#endif - - vec2 sin_t, cos_t; - sincos_taylor_fp64(t, sin_t, cos_t); - - - - vec2 result = vec2(0.0, 0.0); - if (j == 0) { - if (k > 0) { - result = sum_fp64(mul_fp64(u, sin_t), mul_fp64(v, cos_t)); - } else { - result = sub_fp64(mul_fp64(u, sin_t), mul_fp64(v, cos_t)); - } - } else if (j == 1) { - if (k > 0) { - result = sub_fp64(mul_fp64(u, cos_t), mul_fp64(v, sin_t)); - } else { - result = sum_fp64(mul_fp64(u, cos_t), mul_fp64(v, sin_t)); - } - } else if (j == -1) { - if (k > 0) { - result = sub_fp64(mul_fp64(v, sin_t), mul_fp64(u, cos_t)); - } else { - result = -sum_fp64(mul_fp64(v, sin_t), mul_fp64(u, cos_t)); - } - } else { - if (k > 0) { - result = -sum_fp64(mul_fp64(u, sin_t), mul_fp64(v, cos_t)); - } else { - result = sub_fp64(mul_fp64(v, cos_t), mul_fp64(u, sin_t)); - } - } - - return result; -} - -vec2 cos_fp64(vec2 a) { - if (a.x == 0.0 && a.y == 0.0) { - return vec2(1.0, 0.0); - } - - // 2pi range reduction - vec2 z = nint_fp64(div_fp64(a, TWO_PI_FP64)); - vec2 r = sub_fp64(a, mul_fp64(TWO_PI_FP64, z)); - - vec2 t; - float q = floor(r.x / PI_2_FP64.x + 0.5); - int j = int(q); - - if (j < -2 || j > 2) { - return vec2(0.0 / 0.0, 0.0 / 0.0); - } - - t = sub_fp64(r, mul_fp64(PI_2_FP64, vec2(q, 0.0))); - - q = floor(t.x / PI_16_FP64.x + 0.5); - int k = int(q); - - if (k == 0) { - if (j == 0) { - return cos_taylor_fp64(t); - } else if (j == 1) { - return -sin_taylor_fp64(t); - } else if (j == -1) { - return sin_taylor_fp64(t); - } else { - return -cos_taylor_fp64(t); - } - } - - int abs_k = int(abs(float(k))); - - if (abs_k > 4) { - return vec2(0.0 / 0.0, 0.0 / 0.0); - } else { - t = sub_fp64(t, mul_fp64(PI_16_FP64, vec2(q, 0.0))); - } - - vec2 u = vec2(0.0, 0.0); - vec2 v = vec2(0.0, 0.0); - -#if defined(NVIDIA_FP64_WORKAROUND) || defined(INTEL_FP64_WORKAROUND) - if (abs(float(abs_k) - 1.0) < 0.5) { - u = COS_TABLE_0_FP64; - v = SIN_TABLE_0_FP64; - } else if (abs(float(abs_k) - 2.0) < 0.5) { - u = COS_TABLE_1_FP64; - v = SIN_TABLE_1_FP64; - } else if (abs(float(abs_k) - 3.0) < 0.5) { - u = COS_TABLE_2_FP64; - v = SIN_TABLE_2_FP64; - } else if (abs(float(abs_k) - 4.0) < 0.5) { - u = COS_TABLE_3_FP64; - v = SIN_TABLE_3_FP64; - } -#else - if (abs_k == 1) { - u = COS_TABLE_0_FP64; - v = SIN_TABLE_0_FP64; - } else if (abs_k == 2) { - u = COS_TABLE_1_FP64; - v = SIN_TABLE_1_FP64; - } else if (abs_k == 3) { - u = COS_TABLE_2_FP64; - v = SIN_TABLE_2_FP64; - } else if (abs_k == 4) { - u = COS_TABLE_3_FP64; - v = SIN_TABLE_3_FP64; - } -#endif - - vec2 sin_t, cos_t; - sincos_taylor_fp64(t, sin_t, cos_t); - - vec2 result = vec2(0.0, 0.0); - if (j == 0) { - if (k > 0) { - result = sub_fp64(mul_fp64(u, cos_t), mul_fp64(v, sin_t)); - } else { - result = sum_fp64(mul_fp64(u, cos_t), mul_fp64(v, sin_t)); - } - } else if (j == 1) { - if (k > 0) { - result = -sum_fp64(mul_fp64(u, sin_t), mul_fp64(v, cos_t)); - } else { - result = sub_fp64(mul_fp64(v, cos_t), mul_fp64(u, sin_t)); - } - } else if (j == -1) { - if (k > 0) { - result = sum_fp64(mul_fp64(u, sin_t), mul_fp64(v, cos_t)); - } else { - result = sub_fp64(mul_fp64(u, sin_t), mul_fp64(v, cos_t)); - } - } else { - if (k > 0) { - result = sub_fp64(mul_fp64(v, sin_t), mul_fp64(u, cos_t)); - } else { - result = -sum_fp64(mul_fp64(u, cos_t), mul_fp64(v, sin_t)); - } - } - - return result; -} - -vec2 tan_fp64(vec2 a) { - vec2 sin_a; - vec2 cos_a; - - if (a.x == 0.0 && a.y == 0.0) { - return vec2(0.0, 0.0); - } - - // 2pi range reduction - vec2 z = nint_fp64(div_fp64(a, TWO_PI_FP64)); - vec2 r = sub_fp64(a, mul_fp64(TWO_PI_FP64, z)); - - vec2 t; - float q = floor(r.x / PI_2_FP64.x + 0.5); - int j = int(q); - - - if (j < -2 || j > 2) { - return vec2(0.0 / 0.0, 0.0 / 0.0); - } - - t = sub_fp64(r, mul_fp64(PI_2_FP64, vec2(q, 0.0))); - - q = floor(t.x / PI_16_FP64.x + 0.5); - int k = int(q); - int abs_k = int(abs(float(k))); - - // We just can't get PI/16 * 3.0 very accurately. - // so let's just store it - if (abs_k > 4) { - return vec2(0.0 / 0.0, 0.0 / 0.0); - } else { - t = sub_fp64(t, mul_fp64(PI_16_FP64, vec2(q, 0.0))); - } - - - vec2 u = vec2(0.0, 0.0); - vec2 v = vec2(0.0, 0.0); - - vec2 sin_t, cos_t; - vec2 s, c; - sincos_taylor_fp64(t, sin_t, cos_t); - - if (k == 0) { - s = sin_t; - c = cos_t; - } else { -#if defined(NVIDIA_FP64_WORKAROUND) || defined(INTEL_FP64_WORKAROUND) - if (abs(float(abs_k) - 1.0) < 0.5) { - u = COS_TABLE_0_FP64; - v = SIN_TABLE_0_FP64; - } else if (abs(float(abs_k) - 2.0) < 0.5) { - u = COS_TABLE_1_FP64; - v = SIN_TABLE_1_FP64; - } else if (abs(float(abs_k) - 3.0) < 0.5) { - u = COS_TABLE_2_FP64; - v = SIN_TABLE_2_FP64; - } else if (abs(float(abs_k) - 4.0) < 0.5) { - u = COS_TABLE_3_FP64; - v = SIN_TABLE_3_FP64; - } -#else - if (abs_k == 1) { - u = COS_TABLE_0_FP64; - v = SIN_TABLE_0_FP64; - } else if (abs_k == 2) { - u = COS_TABLE_1_FP64; - v = SIN_TABLE_1_FP64; - } else if (abs_k == 3) { - u = COS_TABLE_2_FP64; - v = SIN_TABLE_2_FP64; - } else if (abs_k == 4) { - u = COS_TABLE_3_FP64; - v = SIN_TABLE_3_FP64; - } -#endif - if (k > 0) { - s = sum_fp64(mul_fp64(u, sin_t), mul_fp64(v, cos_t)); - c = sub_fp64(mul_fp64(u, cos_t), mul_fp64(v, sin_t)); - } else { - s = sub_fp64(mul_fp64(u, sin_t), mul_fp64(v, cos_t)); - c = sum_fp64(mul_fp64(u, cos_t), mul_fp64(v, sin_t)); - } - } - - if (j == 0) { - sin_a = s; - cos_a = c; - } else if (j == 1) { - sin_a = c; - cos_a = -s; - } else if (j == -1) { - sin_a = -c; - cos_a = s; - } else { - sin_a = -s; - cos_a = -c; - } - return div_fp64(sin_a, cos_a); -} - -vec2 radians_fp64(vec2 degree) { - return mul_fp64(degree, PI_180_FP64); -} - -vec2 mix_fp64(vec2 a, vec2 b, float x) { - vec2 range = sub_fp64(b, a); - return sum_fp64(a, mul_fp64(range, vec2(x, 0.0))); -} - -// Vector functions -// vec2 functions -void vec2_sum_fp64(vec2 a[2], vec2 b[2], out vec2 out_val[2]) { - out_val[0] = sum_fp64(a[0], b[0]); - out_val[1] = sum_fp64(a[1], b[1]); -} - -void vec2_sub_fp64(vec2 a[2], vec2 b[2], out vec2 out_val[2]) { - out_val[0] = sub_fp64(a[0], b[0]); - out_val[1] = sub_fp64(a[1], b[1]); -} - -void vec2_mul_fp64(vec2 a[2], vec2 b[2], out vec2 out_val[2]) { - out_val[0] = mul_fp64(a[0], b[0]); - out_val[1] = mul_fp64(a[1], b[1]); -} - -void vec2_div_fp64(vec2 a[2], vec2 b[2], out vec2 out_val[2]) { - out_val[0] = div_fp64(a[0], b[0]); - out_val[1] = div_fp64(a[1], b[1]); -} - -void vec2_mix_fp64(vec2 x[2], vec2 y[2], float a, out vec2 out_val[2]) { - vec2 range[2]; - vec2_sub_fp64(y, x, range); - vec2 portion[2]; - portion[0] = range[0] * a; - portion[1] = range[1] * a; - vec2_sum_fp64(x, portion, out_val); -} - -vec2 vec2_length_fp64(vec2 x[2]) { - return sqrt_fp64(sum_fp64(mul_fp64(x[0], x[0]), mul_fp64(x[1], x[1]))); -} - -void vec2_normalize_fp64(vec2 x[2], out vec2 out_val[2]) { - vec2 length = vec2_length_fp64(x); - vec2 length_vec2[2]; - length_vec2[0] = length; - length_vec2[1] = length; - - vec2_div_fp64(x, length_vec2, out_val); -} - -vec2 vec2_distance_fp64(vec2 x[2], vec2 y[2]) { - vec2 diff[2]; - vec2_sub_fp64(x, y, diff); - return vec2_length_fp64(diff); -} - -vec2 vec2_dot_fp64(vec2 a[2], vec2 b[2]) { - vec2 v[2]; - - v[0] = mul_fp64(a[0], b[0]); - v[1] = mul_fp64(a[1], b[1]); - - return sum_fp64(v[0], v[1]); -} - -// vec3 functions -void vec3_sub_fp64(vec2 a[3], vec2 b[3], out vec2 out_val[3]) { - for (int i = 0; i < 3; i++) { - out_val[i] = sum_fp64(a[i], b[i]); - } -} - -void vec3_sum_fp64(vec2 a[3], vec2 b[3], out vec2 out_val[3]) { - for (int i = 0; i < 3; i++) { - out_val[i] = sum_fp64(a[i], b[i]); - } -} - -vec2 vec3_length_fp64(vec2 x[3]) { - return sqrt_fp64(sum_fp64(sum_fp64(mul_fp64(x[0], x[0]), mul_fp64(x[1], x[1])), - mul_fp64(x[2], x[2]))); -} - -vec2 vec3_distance_fp64(vec2 x[3], vec2 y[3]) { - vec2 diff[3]; - vec3_sub_fp64(x, y, diff); - return vec3_length_fp64(diff); -} - -// vec4 functions -void vec4_fp64(vec4 a, out vec2 out_val[4]) { - out_val[0].x = a[0]; - out_val[0].y = 0.0; - - out_val[1].x = a[1]; - out_val[1].y = 0.0; - - out_val[2].x = a[2]; - out_val[2].y = 0.0; - - out_val[3].x = a[3]; - out_val[3].y = 0.0; -} - -void vec4_scalar_mul_fp64(vec2 a[4], vec2 b, out vec2 out_val[4]) { - out_val[0] = mul_fp64(a[0], b); - out_val[1] = mul_fp64(a[1], b); - out_val[2] = mul_fp64(a[2], b); - out_val[3] = mul_fp64(a[3], b); -} - -void vec4_sum_fp64(vec2 a[4], vec2 b[4], out vec2 out_val[4]) { - for (int i = 0; i < 4; i++) { - out_val[i] = sum_fp64(a[i], b[i]); - } -} - -void vec4_dot_fp64(vec2 a[4], vec2 b[4], out vec2 out_val) { - vec2 v[4]; - - v[0] = mul_fp64(a[0], b[0]); - v[1] = mul_fp64(a[1], b[1]); - v[2] = mul_fp64(a[2], b[2]); - v[3] = mul_fp64(a[3], b[3]); - - out_val = sum_fp64(sum_fp64(v[0], v[1]), sum_fp64(v[2], v[3])); -} - -void mat4_vec4_mul_fp64(vec2 b[16], vec2 a[4], out vec2 out_val[4]) { - vec2 tmp[4]; - - for (int i = 0; i < 4; i++) - { - for (int j = 0; j < 4; j++) - { - tmp[j] = b[j + i * 4]; - } - vec4_dot_fp64(a, tmp, out_val[i]); - } -} -// END MODULE_fp64 - -#define MODULE_PROJECT64 - -const vec2 WORLD_SCALE_FP64 = vec2(81.4873275756836, 0.0000032873668232014097); - -uniform vec2 projectionScaleFP64; -uniform vec2 projectionFP64[16]; - -// longitude: lnglat_fp64.xy; latitude: lnglat_fp64.zw -void mercatorProject_fp64(vec4 lnglat_fp64, out vec2 out_val[2]) { - -#if defined(NVIDIA_FP64_WORKAROUND) - out_val[0] = sum_fp64(radians_fp64(lnglat_fp64.xy), PI_FP64 * ONE); -#else - out_val[0] = sum_fp64(radians_fp64(lnglat_fp64.xy), PI_FP64); -#endif - out_val[1] = sub_fp64(PI_FP64, - log_fp64(tan_fp64(sum_fp64(PI_4_FP64, radians_fp64(lnglat_fp64.zw) / 2.0)))); - return; -} - -void project_position_fp64(vec4 position_fp64, out vec2 out_val[2]) { - - vec2 pos_fp64[2]; - mercatorProject_fp64(position_fp64, pos_fp64); - vec2 x_fp64 = mul_fp64(pos_fp64[0], projectionScaleFP64); - vec2 y_fp64 = mul_fp64(pos_fp64[1], projectionScaleFP64); - out_val[0] = mul_fp64(x_fp64, WORLD_SCALE_FP64); - out_val[1] = mul_fp64(y_fp64, WORLD_SCALE_FP64); - - return; -} - -vec4 project_to_clipspace_fp64(vec2 vertex_pos_modelspace[4]) { - vec2 vertex_pos_clipspace[4]; - mat4_vec4_mul_fp64(projectionFP64, vertex_pos_modelspace, vertex_pos_clipspace); - return vec4( - vertex_pos_clipspace[0].x, - vertex_pos_clipspace[1].x, - vertex_pos_clipspace[2].x, - vertex_pos_clipspace[3].x - ); -} -// END MODULE_project64 - -#define SHADER_NAME scatterplot-layer-vertex-shader-64 - -attribute vec3 positions; - -attribute vec3 instancePositions; -attribute vec2 instancePositions64xyLow; -attribute float instanceRadius; -attribute vec4 instanceColors; -attribute vec3 instancePickingColors; - -// Only one-dimensional arrays may be declared in GLSL ES 1.0. specs p.24 -uniform float opacity; -uniform float radiusScale; -uniform float radiusMinPixels; -uniform float radiusMaxPixels; -uniform float renderPickingBuffer; -uniform float outline; -uniform float strokeWidth; - -varying vec4 vColor; -varying vec2 unitPosition; -varying float innerUnitRadius; - -void main(void) { - // Multiply out radius and clamp to limits - float outerRadiusPixels = clamp( - project_scale(radiusScale * instanceRadius), - radiusMinPixels, radiusMaxPixels - ); - - // outline is centered at the radius - // outer radius needs to offset by half stroke width - outerRadiusPixels += outline * strokeWidth / 2.0; - - // position on the containing square in [-1, 1] space - unitPosition = positions.xy; - // 0 - solid circle, 1 - stroke with lineWidth=0 - innerUnitRadius = outline * (1.0 - strokeWidth / outerRadiusPixels); - - vec4 instancePositions64xy = vec4( - instancePositions.x, instancePositions64xyLow.x, - instancePositions.y, instancePositions64xyLow.y); - - vec2 projected_coord_xy[2]; - project_position_fp64(instancePositions64xy, projected_coord_xy); - - vec2 vertex_pos_localspace[4]; - vec4_fp64(vec4(positions * outerRadiusPixels, 0.0), vertex_pos_localspace); - - vec2 vertex_pos_modelspace[4]; - vertex_pos_modelspace[0] = sum_fp64(vertex_pos_localspace[0], projected_coord_xy[0]); - vertex_pos_modelspace[1] = sum_fp64(vertex_pos_localspace[1], projected_coord_xy[1]); - vertex_pos_modelspace[2] = sum_fp64(vertex_pos_localspace[2], - vec2(project_scale(instancePositions.z), 0.0)); - vertex_pos_modelspace[3] = vec2(1.0, 0.0); - - gl_Position = project_to_clipspace_fp64(vertex_pos_modelspace); - - if (renderPickingBuffer > 0.5) { - vColor = vec4(instancePickingColors / 255., 1.); - } else { - vColor = vec4(instanceColors.rgb, instanceColors.a * opacity) / 255.; - } - // // Apply opacity to instance color, or return instance picking color - // vec4 color = vec4(instanceColors.rgb, instanceColors.a * opacity) / 255.; - // vec4 pickingColor = vec4(instancePickingColors / 255., 1.); - // vColor = mix(color, pickingColor, renderPickingBuffer); -} -`; - -const SHADER_TYPE = 35633; - -const ERROR_LOG = `\ -WARNING: 0:264: '/' : Zero divided by zero during constant folding generated NaN -WARNING: 0:264: '/' : Zero divided by zero during constant folding generated NaN -WARNING: 0:294: '/' : Divide by zero during constant folding -WARNING: 0:294: '/' : Divide by zero during constant folding -WARNING: 0:344: '/' : Zero divided by zero during constant folding generated NaN -WARNING: 0:344: '/' : Zero divided by zero during constant folding generated NaN -WARNING: 0:447: '/' : Zero divided by zero during constant folding generated NaN -WARNING: 0:447: '/' : Zero divided by zero during constant folding generated NaN -WARNING: 0:470: '/' : Zero divided by zero during constant folding generated NaN -WARNING: 0:470: '/' : Zero divided by zero during constant folding generated NaN -WARNING: 0:557: '/' : Zero divided by zero during constant folding generated NaN -WARNING: 0:557: '/' : Zero divided by zero during constant folding generated NaN -WARNING: 0:580: '/' : Zero divided by zero during constant folding generated NaN -WARNING: 0:580: '/' : Zero divided by zero during constant folding generated NaN -WARNING: 0:669: '/' : Zero divided by zero during constant folding generated NaN -WARNING: 0:669: '/' : Zero divided by zero during constant folding generated NaN -WARNING: 0:681: '/' : Zero divided by zero during constant folding generated NaN -WARNING: 0:681: '/' : Zero divided by zero during constant folding generated NaN -ERROR: 0:967: 'project_scale' : no matching overloaded function found -ERROR: 0:994: 'project_scale' : no matching overloaded function found -`; - -const EXPECTED = `\ -GLSL compilation error in vertex shader scatterplot-layer-vertex-shader-64 - - 965: radiusMinPixels, radiusMaxPixels - 966: ); - 967: -^^^ ERROR: 'project_scale' : no matching overloaded function found - - 992: vertex_pos_modelspace[3] = vec2(1.0, 0.0); - 993: - 994: gl_Position = project_to_clipspace_fp64(vertex_pos_modelspace); -^^^ ERROR: 'project_scale' : no matching overloaded function found - - - 262: - 263: float x = 1.0 / sqrt(a.x); - 264: float yn = a.x * x; -^^^ WARNING: '/' : Zero divided by zero during constant folding generated NaN - - 292: if (a.x == 0.0 && a.y == 0.0) return vec2(1.0, 0.0); - 293: if (a.x == 1.0 && a.y == 0.0) return E_FP64; - 294: -^^^ WARNING: '/' : Divide by zero during constant folding - - 342: vec2 x = vec2(log(a.x), 0.0); - 343: vec2 s; - 344: #if defined(NVIDIA_FP64_WORKAROUND) || defined(INTEL_FP64_WORKAROUND) -^^^ WARNING: '/' : Zero divided by zero during constant folding generated NaN - - 445: } - 446: - 447: t = sub_fp64(r, mul_fp64(PI_2_FP64, vec2(q, 0.0))); -^^^ WARNING: '/' : Zero divided by zero during constant folding generated NaN - - 468: } else { - 469: t = sub_fp64(t, mul_fp64(PI_16_FP64, vec2(q, 0.0))); - 470: } -^^^ WARNING: '/' : Zero divided by zero during constant folding generated NaN - - 555: } - 556: - 557: t = sub_fp64(r, mul_fp64(PI_2_FP64, vec2(q, 0.0))); -^^^ WARNING: '/' : Zero divided by zero during constant folding generated NaN - - 578: } else { - 579: t = sub_fp64(t, mul_fp64(PI_16_FP64, vec2(q, 0.0))); - 580: } -^^^ WARNING: '/' : Zero divided by zero during constant folding generated NaN - - 667: } - 668: - 669: t = sub_fp64(r, mul_fp64(PI_2_FP64, vec2(q, 0.0))); -^^^ WARNING: '/' : Zero divided by zero during constant folding generated NaN - - 679: } else { - 680: t = sub_fp64(t, mul_fp64(PI_16_FP64, vec2(q, 0.0))); - 681: } -^^^ WARNING: '/' : Zero divided by zero during constant folding generated NaN - -`; - -test('WebGL#formatGLSLError', (t) => { - const error = formatGLSLError(ERROR_LOG, SHADER_SOURCE, SHADER_TYPE); - t.equal(error, EXPECTED, 'formatGLSLError generated correct error'); - t.end(); -}); diff --git a/modules/webgl/test/glsl-utils/get-shader-name.spec.js b/modules/webgl/test/glsl-utils/get-shader-name.spec.js deleted file mode 100644 index a84c24cf2..000000000 --- a/modules/webgl/test/glsl-utils/get-shader-name.spec.js +++ /dev/null @@ -1,20 +0,0 @@ -import getShaderName from '@luma.gl/webgl/glsl-utils/get-shader-name'; -import test from 'tape-promise/tape'; - -const SHADER_1 = `\ -uniform float floaty; -#define SHADER_NAME name-of-shader -main() {} -`; - -const SHADER_2 = `\ -uniform float floaty; -#define SHADER name -main() {} -`; - -test('WebGL#getShaderName', (t) => { - t.equal(getShaderName(SHADER_1), 'name-of-shader', 'getShaderName extracted correct name'); - t.equal(getShaderName(SHADER_2), 'unnamed', 'getShaderName extracted default name'); - t.end(); -}); diff --git a/modules/webgl/test/glsl-utils/get-shader-version.spec.js b/modules/webgl/test/glsl-utils/get-shader-version.spec.js deleted file mode 100644 index fe4c9bf2f..000000000 --- a/modules/webgl/test/glsl-utils/get-shader-version.spec.js +++ /dev/null @@ -1,40 +0,0 @@ -import test from 'tape-promise/tape'; -import getShaderVersion from '@luma.gl/webgl/glsl-utils/get-shader-version'; - -const SHADER1 = 'void main() {}'; -const SHADER2 = '#version 100 void main() {}'; -const SHADER3 = `\ -void main() { - -}`; -const SHADER4 = `\ -#version 300 es -void main() { - -}`; -const SHADER5 = `#version 300 es -void main() { - -}`; -const SHADER6 = `\ -#version 100 -void main() { - -}`; -const SHADER7 = '#version 300 es void main() {}'; -const versionTests = { - [SHADER1]: 100, - [SHADER2]: 100, - [SHADER3]: 100, - [SHADER4]: 300, - [SHADER5]: 300, - [SHADER6]: 100, - [SHADER7]: 300 -}; - -test('Shader-utils#getShaderVersion', (t) => { - for (const string in versionTests) { - t.equal(getShaderVersion(string), versionTests[string], 'Version should match'); - } - t.end(); -}); diff --git a/modules/webgl/test/glsl-utils/index.js b/modules/webgl/test/glsl-utils/index.js deleted file mode 100644 index a3b9c7ea5..000000000 --- a/modules/webgl/test/glsl-utils/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import './format-glsl-error.spec'; -import './get-shader-name.spec'; -import './get-shader-version.spec'; diff --git a/modules/webgl/test/index.js b/modules/webgl/test/index.js index ae684709e..22c652144 100644 --- a/modules/webgl/test/index.js +++ b/modules/webgl/test/index.js @@ -1,4 +1,4 @@ import './utils'; -import './glsl-utils'; +import './webgl-utils/parse-shader-compiler-log.spec'; import './classes'; import './features'; diff --git a/modules/webgl/test/webgl-utils/parse-shader-compiler-log.spec.js b/modules/webgl/test/webgl-utils/parse-shader-compiler-log.spec.js new file mode 100644 index 000000000..d76ef3eed --- /dev/null +++ b/modules/webgl/test/webgl-utils/parse-shader-compiler-log.spec.js @@ -0,0 +1,162 @@ +import test from 'tape-promise/tape'; +import {parseShaderCompilerLog} from '@luma.gl/webgl/webgl-utils/parse-shader-compiler-log'; + +test('parseShaderCompilerLog', (t) => { + const {ERROR_LOG, EXPECTED_MESSAGES} = getFixtures(); + const messages = parseShaderCompilerLog(ERROR_LOG); // SHADER_SOURCE, SHADER_TYPE; + t.deepEqual(messages, EXPECTED_MESSAGES, 'parseShaderCompilerLog generated correct messages'); + t.end(); +}); + +const ERROR_LOG = `\ +WARNING: 0:264: '/' : Zero divided by zero during constant folding generated NaN +WARNING: 0:264: '/' : Zero divided by zero during constant folding generated NaN +WARNING: 0:294: '/' : Divide by zero during constant folding +WARNING: 0:294: '/' : Divide by zero during constant folding +WARNING: 0:344: '/' : Zero divided by zero during constant folding generated NaN +WARNING: 0:344: '/' : Zero divided by zero during constant folding generated NaN +WARNING: 0:447: '/' : Zero divided by zero during constant folding generated NaN +WARNING: 0:447: '/' : Zero divided by zero during constant folding generated NaN +WARNING: 0:470: '/' : Zero divided by zero during constant folding generated NaN +WARNING: 0:470: '/' : Zero divided by zero during constant folding generated NaN +WARNING: 0:557: '/' : Zero divided by zero during constant folding generated NaN +WARNING: 0:557: '/' : Zero divided by zero during constant folding generated NaN +WARNING: 0:580: '/' : Zero divided by zero during constant folding generated NaN +WARNING: 0:580: '/' : Zero divided by zero during constant folding generated NaN +WARNING: 0:669: '/' : Zero divided by zero during constant folding generated NaN +WARNING: 0:669: '/' : Zero divided by zero during constant folding generated NaN +WARNING: 0:681: '/' : Zero divided by zero during constant folding generated NaN +WARNING: 0:681: '/' : Zero divided by zero during constant folding generated NaN +ERROR: 0:967: 'project_scale' : no matching overloaded function found +ERROR: 0:994: 'project_scale' : no matching overloaded function found +`; + +const EXPECTED_MESSAGES = [ + { + message: "'/' : Zero divided by zero during constant folding generated NaN", + type: 'warning', + lineNum: 264, + linePos: 0 + }, + { + message: "'/' : Zero divided by zero during constant folding generated NaN", + type: 'warning', + lineNum: 264, + linePos: 0 + }, + { + message: "'/' : Divide by zero during constant folding", + type: 'warning', + lineNum: 294, + linePos: 0 + }, + { + message: "'/' : Divide by zero during constant folding", + type: 'warning', + lineNum: 294, + linePos: 0 + }, + { + message: "'/' : Zero divided by zero during constant folding generated NaN", + type: 'warning', + lineNum: 344, + linePos: 0 + }, + { + message: "'/' : Zero divided by zero during constant folding generated NaN", + type: 'warning', + lineNum: 344, + linePos: 0 + }, + { + message: "'/' : Zero divided by zero during constant folding generated NaN", + type: 'warning', + lineNum: 447, + linePos: 0 + }, + { + message: "'/' : Zero divided by zero during constant folding generated NaN", + type: 'warning', + lineNum: 447, + linePos: 0 + }, + { + message: "'/' : Zero divided by zero during constant folding generated NaN", + type: 'warning', + lineNum: 470, + linePos: 0 + }, + { + message: "'/' : Zero divided by zero during constant folding generated NaN", + type: 'warning', + lineNum: 470, + linePos: 0 + }, + { + message: "'/' : Zero divided by zero during constant folding generated NaN", + type: 'warning', + lineNum: 557, + linePos: 0 + }, + { + message: "'/' : Zero divided by zero during constant folding generated NaN", + type: 'warning', + lineNum: 557, + linePos: 0 + }, + { + message: "'/' : Zero divided by zero during constant folding generated NaN", + type: 'warning', + lineNum: 580, + linePos: 0 + }, + { + message: "'/' : Zero divided by zero during constant folding generated NaN", + type: 'warning', + lineNum: 580, + linePos: 0 + }, + { + message: "'/' : Zero divided by zero during constant folding generated NaN", + type: 'warning', + lineNum: 669, + linePos: 0 + }, + { + message: "'/' : Zero divided by zero during constant folding generated NaN", + type: 'warning', + lineNum: 669, + linePos: 0 + }, + { + message: "'/' : Zero divided by zero during constant folding generated NaN", + type: 'warning', + lineNum: 681, + linePos: 0 + }, + { + message: "'/' : Zero divided by zero during constant folding generated NaN", + type: 'warning', + lineNum: 681, + linePos: 0 + }, + { + message: "'project_scale' : no matching overloaded function found", + type: 'error', + lineNum: 967, + linePos: 0 + }, + { + message: "'project_scale' : no matching overloaded function found", + type: 'error', + lineNum: 994, + linePos: 0 + } +]; + +function getFixtures() { + return { + ERROR_LOG, + EXPECTED_MESSAGES + }; +}