From 2d92cdd4043b5dd53a66c8f0ddf496ae8b08a8fe Mon Sep 17 00:00:00 2001 From: Liam Don Date: Thu, 28 Dec 2023 10:41:05 -0800 Subject: [PATCH 01/43] Support for integer/data textures in WebGL2 and WebGPU --- src/platform/graphics/constants.js | 338 ++++++++++++++++-- src/platform/graphics/shader-processor.js | 15 +- src/platform/graphics/texture.js | 12 +- .../graphics/uniform-buffer-format.js | 95 +++-- src/platform/graphics/uniform-buffer.js | 149 ++++---- .../graphics/webgl/webgl-graphics-device.js | 187 +++++++--- .../graphics/webgl/webgl-shader-input.js | 17 +- src/platform/graphics/webgl/webgl-shader.js | 20 +- src/platform/graphics/webgl/webgl-texture.js | 102 +++++- src/platform/graphics/webgpu/constants.js | 23 +- .../graphics/webgpu/webgpu-texture.js | 4 +- 11 files changed, 765 insertions(+), 197 deletions(-) diff --git a/src/platform/graphics/constants.js b/src/platform/graphics/constants.js index 70406b9fa5a..7222594f446 100644 --- a/src/platform/graphics/constants.js +++ b/src/platform/graphics/constants.js @@ -618,6 +618,133 @@ export const PIXELFORMAT_ATC_RGBA = 30; */ export const PIXELFORMAT_BGRA8 = 31; +/** + * 8-bit signed integer single-channel (R) format (WebGL2 only). + * + * @type {number} + */ +export const PIXELFORMAT_R8I = 32; + +/** + * 8-bit unsigned integer single-channel (R) format (WebGL2 only). + * + * @type {number} + */ +export const PIXELFORMAT_R8UI = 33; + +/** + * 16-bit signed integer single-channel (R) format (WebGL2 only). + * + * @type {number} + */ +export const PIXELFORMAT_R16I = 34; + +/** + * 16-bit unsigned integer single-channel (R) format (WebGL2 only). + * + * @type {number} + */ +export const PIXELFORMAT_R16UI = 35; + +/** + * 32-bit signed integer single-channel (R) format (WebGL2 only). + * + * @type {number} + */ +export const PIXELFORMAT_R32I = 36; + +/** + * 32-bit unsigned integer single-channel (R) format (WebGL2 only). + * + * @type {number} + */ +export const PIXELFORMAT_R32UI = 37; + +/** + * 8-bit per-channel signed integer (RG) format (WebGL2 only). + * + * @type {number} + */ +export const PIXELFORMAT_RG8I = 38; + +/** + * 8-bit per-channel unsigned integer (RG) format (WebGL2 only). + * + * @type {number} + */ +export const PIXELFORMAT_RG8UI = 39; + +/** + * 16-bit per-channel signed integer (RG) format (WebGL2 only). + * + * @type {number} + */ +export const PIXELFORMAT_RG16I = 40; + +/** + * 16-bit per-channel unsigned integer (RG) format (WebGL2 only). + * + * @type {number} + */ +export const PIXELFORMAT_RG16UI = 41; + +/** + * 32-bit per-channel signed integer (RG) format (WebGL2 only). + * + * @type {number} + */ +export const PIXELFORMAT_RG32I = 42; + +/** + * 32-bit per-channel unsigned integer (RG) format (WebGL2 only). + * + * @type {number} + */ +export const PIXELFORMAT_RG32UI = 43; + +/** + * 8-bit per-channel signed integer (RGBA) format (WebGL2 only). + * + * @type {number} + */ +export const PIXELFORMAT_RGBA8I = 44; + +/** + * 8-bit per-channel unsigned integer (RGBA) format (WebGL2 only). + * + * @type {number} + */ +export const PIXELFORMAT_RGBA8UI = 45; + +/** + * 16-bit per-channel signed integer (RGBA) format (WebGL2 only). + * + * @type {number} + */ +export const PIXELFORMAT_RGBA16I = 46; + +/** + * 16-bit per-channel unsigned integer (RGBA) format (WebGL2 only). + * + * @type {number} + */ +export const PIXELFORMAT_RGBA16UI = 47; + +/** + * 32-bit per-channel signed integer (RGBA) format (WebGL2 only). + * + * @type {number} + */ +export const PIXELFORMAT_RGBA32I = 48; + +/** + * 32-bit per-channel unsigned integer (RGBA) format (WebGL2 only). + * + * @type {number} + */ +export const PIXELFORMAT_RGBA32UI = 49; + + // map of engine PIXELFORMAT_*** enums to information about the format export const pixelFormatInfo = new Map([ @@ -655,7 +782,27 @@ export const pixelFormatInfo = new Map([ [PIXELFORMAT_PVRTC_4BPP_RGBA_1, { name: 'PVRTC_4BPP_RGBA_1', blockSize: 8 }], [PIXELFORMAT_ASTC_4x4, { name: 'ASTC_4x4', blockSize: 16 }], [PIXELFORMAT_ATC_RGB, { name: 'ATC_RGB', blockSize: 8 }], - [PIXELFORMAT_ATC_RGBA, { name: 'ATC_RGBA', blockSize: 16 }] + [PIXELFORMAT_ATC_RGBA, { name: 'ATC_RGBA', blockSize: 16 }], + + // uncompressed integer formats (WebGL2 Only) + [PIXELFORMAT_R8I, { name: 'R8I', size: 1, channelSize: 1 }], + [PIXELFORMAT_R8UI, { name: 'R8UI', size: 1, channelSize: 1 }], + [PIXELFORMAT_R16I, { name: 'R16I', size: 2, channelSize: 2 }], + [PIXELFORMAT_R16UI, { name: 'R16UI', size: 2, channelSize: 2 }], + [PIXELFORMAT_R32I, { name: 'R32I', size: 4, channelSize: 4 }], + [PIXELFORMAT_R32UI, { name: 'R32UI', size: 4, channelSize: 4 }], + [PIXELFORMAT_RG8I, { name: 'RG8I', size: 2, channelSize: 1 }], + [PIXELFORMAT_RG8UI, { name: 'RG8UI', size: 2, channelSize: 1 }], + [PIXELFORMAT_RG16I, { name: 'RG8I', size: 4, channelSize: 2 }], + [PIXELFORMAT_RG16UI, { name: 'RG16UI', size: 4, channelSize: 2 }], + [PIXELFORMAT_RG32I, { name: 'RG32I', size: 8, channelSize: 4 }], + [PIXELFORMAT_RG32UI, { name: 'RG32UI', size: 8, channelSize: 4 }], + [PIXELFORMAT_RGBA8I, { name: 'RGBA8I', size: 4, channelSize: 1 }], + [PIXELFORMAT_RGBA8UI, { name: 'RGBA8UI', size: 4, channelSize: 1 }], + [PIXELFORMAT_RGBA16I, { name: 'RGBA16I', size: 8, channelSize: 2 }], + [PIXELFORMAT_RGBA16UI, { name: 'RGBA16UI', size: 8, channelSize: 2 }], + [PIXELFORMAT_RGBA32I, { name: 'RGBA32I', size: 16, channelSize: 4 }], + [PIXELFORMAT_RGBA32UI, { name: 'RGBA32UI', size: 16, channelSize: 4 }] ]); // update this function when exposing additional compressed pixel formats @@ -663,18 +810,41 @@ export const isCompressedPixelFormat = (format) => { return pixelFormatInfo.get(format).blockSize !== undefined; }; +export const isIntegerPixelFormat = (format) => { + return pixelFormatInfo.get(format)?.channelSize !== undefined; +}; + // get the pixel format array type export const getPixelFormatArrayType = (format) => { switch (format) { case PIXELFORMAT_RGB32F: case PIXELFORMAT_RGBA32F: return Float32Array; + case PIXELFORMAT_R32I: + case PIXELFORMAT_RG32I: + case PIXELFORMAT_RGBA32I: + return Int32Array; + case PIXELFORMAT_R32UI: + case PIXELFORMAT_RG32UI: + case PIXELFORMAT_RGBA32UI: + return Uint32Array; + case PIXELFORMAT_R16I: + case PIXELFORMAT_RG16I: + case PIXELFORMAT_RGBA16I: + return Int16Array; + case PIXELFORMAT_R16UI: + case PIXELFORMAT_RG16UI: + case PIXELFORMAT_RGBA16UI: case PIXELFORMAT_RGB565: case PIXELFORMAT_RGBA5551: case PIXELFORMAT_RGBA4: case PIXELFORMAT_RGB16F: case PIXELFORMAT_RGBA16F: return Uint16Array; + case PIXELFORMAT_R8I: + case PIXELFORMAT_RG8I: + case PIXELFORMAT_RGBA8I: + return Int8Array; default: return Uint8Array; } @@ -1169,58 +1339,150 @@ export const TYPE_FLOAT32 = 6; */ export const TYPE_FLOAT16 = 7; -export const UNIFORMTYPE_BOOL = 0; +/** + * Uniform and sampler types + */ +// Uniforms +export const UNIFORMTYPE_FLOAT = 0; export const UNIFORMTYPE_INT = 1; -export const UNIFORMTYPE_FLOAT = 2; -export const UNIFORMTYPE_VEC2 = 3; -export const UNIFORMTYPE_VEC3 = 4; -export const UNIFORMTYPE_VEC4 = 5; -export const UNIFORMTYPE_IVEC2 = 6; -export const UNIFORMTYPE_IVEC3 = 7; -export const UNIFORMTYPE_IVEC4 = 8; -export const UNIFORMTYPE_BVEC2 = 9; -export const UNIFORMTYPE_BVEC3 = 10; -export const UNIFORMTYPE_BVEC4 = 11; -export const UNIFORMTYPE_MAT2 = 12; -export const UNIFORMTYPE_MAT3 = 13; -export const UNIFORMTYPE_MAT4 = 14; -export const UNIFORMTYPE_TEXTURE2D = 15; -export const UNIFORMTYPE_TEXTURECUBE = 16; -export const UNIFORMTYPE_FLOATARRAY = 17; -export const UNIFORMTYPE_TEXTURE2D_SHADOW = 18; -export const UNIFORMTYPE_TEXTURECUBE_SHADOW = 19; -export const UNIFORMTYPE_TEXTURE3D = 20; -export const UNIFORMTYPE_VEC2ARRAY = 21; -export const UNIFORMTYPE_VEC3ARRAY = 22; -export const UNIFORMTYPE_VEC4ARRAY = 23; -export const UNIFORMTYPE_MAT4ARRAY = 24; -export const UNIFORMTYPE_TEXTURE2D_ARRAY = 25; +export const UNIFORMTYPE_UINT = 2; +export const UNIFORMTYPE_BOOL = 3; + +export const UNIFORMTYPE_VEC2 = 4; +export const UNIFORMTYPE_IVEC2 = 5; +export const UNIFORMTYPE_UVEC2 = 6; +export const UNIFORMTYPE_BVEC2 = 7; + +export const UNIFORMTYPE_VEC3 = 8; +export const UNIFORMTYPE_IVEC3 = 9; +export const UNIFORMTYPE_UVEC3 = 10; +export const UNIFORMTYPE_BVEC3 = 11; + +export const UNIFORMTYPE_VEC4 = 12; +export const UNIFORMTYPE_IVEC4 = 13; +export const UNIFORMTYPE_UVEC4 = 14; +export const UNIFORMTYPE_BVEC4 = 15; + +export const UNIFORMTYPE_MAT2 = 16; +export const UNIFORMTYPE_MAT3 = 17; +export const UNIFORMTYPE_MAT4 = 18; + +// Uniform buffers +export const UNIFORMTYPE_FLOATARRAY = 19; +export const UNIFORMTYPE_INTARRAY = 20; +export const UNIFORMTYPE_UINTARRAY = 21; +export const UNIFORMTYPE_BOOLARRAY = 22; + +export const UNIFORMTYPE_VEC2ARRAY = 23; +export const UNIFORMTYPE_IVEC2ARRAY = 24; +export const UNIFORMTYPE_UVEC2ARRAY = 25; +export const UNIFORMTYPE_BVEC2ARRAY = 26; + +export const UNIFORMTYPE_VEC3ARRAY = 27; +export const UNIFORMTYPE_IVEC3ARRAY = 28; +export const UNIFORMTYPE_UVEC3ARRAY = 29; +export const UNIFORMTYPE_BVEC3ARRAY = 30; + +export const UNIFORMTYPE_VEC4ARRAY = 31; +export const UNIFORMTYPE_IVEC4ARRAY = 32; +export const UNIFORMTYPE_UVEC4ARRAY = 33; +export const UNIFORMTYPE_BVEC4ARRAY = 34; + +export const UNIFORMTYPE_MAT4ARRAY = 35; + +// Samplers: 2D +export const UNIFORMTYPE_TEXTURE2D = 36; +export const UNIFORMTYPE_ITEXTURE2D = 37; +export const UNIFORMTYPE_UTEXTURE2D = 38; +export const UNIFORMTYPE_TEXTURE2D_SHADOW = 39; + +// Samplers: Cube +export const UNIFORMTYPE_TEXTURECUBE = 40; +export const UNIFORMTYPE_ITEXTURECUBE = 41; +export const UNIFORMTYPE_UTEXTURECUBE = 42; +export const UNIFORMTYPE_TEXTURECUBE_SHADOW = 43; + +// Samplers: 3D +export const UNIFORMTYPE_TEXTURE3D = 44; +export const UNIFORMTYPE_ITEXTURE3D = 45; +export const UNIFORMTYPE_UTEXTURE3D = 46; + +// Samplers Texture Array +export const UNIFORMTYPE_TEXTURE2D_ARRAY = 47; +export const UNIFORMTYPE_ITEXTURE2D_ARRAY = 48; +export const UNIFORMTYPE_UTEXTURE2D_ARRAY = 49; + export const uniformTypeToName = [ - 'bool', - 'int', + // Uniforms 'float', + 'int', + 'uint', + 'bool', + 'vec2', - 'vec3', - 'vec4', 'ivec2', + 'uvec2', + 'bvec2', + + 'vec3', 'ivec3', + 'uvec3', + 'bvec3', + + 'vec4', 'ivec4', - 'bec2', - 'bec3', - 'bec4', + 'uvec4', + 'bvec4', + 'mat2', 'mat3', 'mat4', - 'sampler2D', - 'samplerCube', + + // Uniform buffers '', // not directly handled: UNIFORMTYPE_FLOATARRAY + '', // not directly handled: UNIFORMTYPE_INTARRAY + '', // not directly handled: UNIFORMTYPE_UINTARRAY + '', // not directly handled: UNIFORMTYPE_BOOLARRAY + + '', // not directly handled: UNIFORMTYPE_VEC2ARRAY + '', // not directly handled: UNIFORMTYPE_IVEC2ARRAY + '', // not directly handled: UNIFORMTYPE_UVEC2ARRAY + '', // not directly handled: UNIFORMTYPE_BVEC2ARRAY + + '', // not directly handled: UNIFORMTYPE_VEC3ARRAY + '', // not directly handled: UNIFORMTYPE_IVEC3ARRAY + '', // not directly handled: UNIFORMTYPE_UVEC3ARRAY + '', // not directly handled: UNIFORMTYPE_BVEC3ARRAY + + '', // not directly handled: UNIFORMTYPE_VEC4ARRAY + '', // not directly handled: UNIFORMTYPE_IVEC4ARRAY + '', // not directly handled: UNIFORMTYPE_UVEC4ARRAY + '', // not directly handled: UNIFORMTYPE_BVEC4ARRAY + + '', // not directly handled: UNIFORMTYPE_MAT4ARRAY + + // Samplers: 2D + 'sampler2D', + 'isampler2D', + 'usampler2D', 'sampler2DShadow', + + // Samplers: Cube + 'samplerCube', + 'isamplerCube', + 'usamplerCube', 'samplerCubeShadow', + + // Samplers: 3D 'sampler3D', - '', // not directly handled: UNIFORMTYPE_VEC2ARRAY - '', // not directly handled: UNIFORMTYPE_VEC3ARRAY - '' // not directly handled: UNIFORMTYPE_VEC4ARRAY + 'isampler3D', + 'usampler3D', + + // Samplers Texture Array + 'sampler2DArray', + 'isampler2DArray', + 'usampler2DArray' ]; /** diff --git a/src/platform/graphics/shader-processor.js b/src/platform/graphics/shader-processor.js index 2044dc2efa9..70402742e2c 100644 --- a/src/platform/graphics/shader-processor.js +++ b/src/platform/graphics/shader-processor.js @@ -25,14 +25,25 @@ const MARKER = '@@@'; const ARRAY_IDENTIFIER = /([\w-]+)\[(.*?)\]/; const precisionQualifiers = new Set(['highp', 'mediump', 'lowp']); -const shadowSamplers = new Set(['sampler2DShadow', 'samplerCubeShadow']); +const shadowSamplers = new Set(['sampler2DShadow', 'samplerCubeShadow', 'sampler2DArrayShadow']); const textureDimensions = { sampler2D: TEXTUREDIMENSION_2D, + isampler2D: TEXTUREDIMENSION_2D, + usampler2D: TEXTUREDIMENSION_2D, + sampler2DShadow: TEXTUREDIMENSION_2D, + sampler3D: TEXTUREDIMENSION_3D, + isampler3D: TEXTUREDIMENSION_3D, + usampler3D: TEXTUREDIMENSION_3D, + samplerCube: TEXTUREDIMENSION_CUBE, + isamplerCube: TEXTUREDIMENSION_CUBE, + usamplerCube: TEXTUREDIMENSION_CUBE, samplerCubeShadow: TEXTUREDIMENSION_CUBE, - sampler2DShadow: TEXTUREDIMENSION_2D, + sampler2DArray: TEXTUREDIMENSION_2D_ARRAY, + isampler2DArray: TEXTUREDIMENSION_2D_ARRAY, + usampler2DArray: TEXTUREDIMENSION_2D_ARRAY, sampler2DArrayShadow: TEXTUREDIMENSION_2D_ARRAY }; diff --git a/src/platform/graphics/texture.js b/src/platform/graphics/texture.js index cfa6d22aaa9..8b85d8c240b 100644 --- a/src/platform/graphics/texture.js +++ b/src/platform/graphics/texture.js @@ -15,7 +15,8 @@ import { TEXHINT_SHADOWMAP, TEXHINT_ASSET, TEXHINT_LIGHTMAP, TEXTURELOCK_WRITE, TEXTUREPROJECTION_NONE, TEXTUREPROJECTION_CUBE, - TEXTURETYPE_DEFAULT, TEXTURETYPE_RGBM, TEXTURETYPE_RGBE, TEXTURETYPE_RGBP, TEXTURETYPE_SWIZZLEGGGR + TEXTURETYPE_DEFAULT, TEXTURETYPE_RGBM, TEXTURETYPE_RGBE, TEXTURETYPE_RGBP, TEXTURETYPE_SWIZZLEGGGR, + isIntegerPixelFormat, FILTER_NEAREST } from './constants.js'; let id = 0; @@ -201,6 +202,12 @@ class Texture { this._format = options.format ?? PIXELFORMAT_RGBA8; this._compressed = isCompressedPixelFormat(this._format); + this._integerFormat = isIntegerPixelFormat(this._format); + if (this._integerFormat) { + options.mipmaps = false; + options.minFilter = FILTER_NEAREST; + options.magFilter = FILTER_NEAREST; + } if (graphicsDevice.supportsVolumeTextures) { this._volume = options.volume ?? false; @@ -721,7 +728,8 @@ class Texture { return (this.format === PIXELFORMAT_RGB16F || this.format === PIXELFORMAT_RGB32F || this.format === PIXELFORMAT_RGBA16F || - this.format === PIXELFORMAT_RGBA32F) ? 'linear' : 'srgb'; + this.format === PIXELFORMAT_RGBA32F || + isIntegerPixelFormat(this.format)) ? 'linear' : 'srgb'; } } diff --git a/src/platform/graphics/uniform-buffer-format.js b/src/platform/graphics/uniform-buffer-format.js index 4c1bd0ba236..456e2e6f9c1 100644 --- a/src/platform/graphics/uniform-buffer-format.js +++ b/src/platform/graphics/uniform-buffer-format.js @@ -2,30 +2,41 @@ import { Debug } from '../../core/debug.js'; import { math } from '../../core/math/math.js'; import { uniformTypeToName, bindGroupNames, - UNIFORMTYPE_BOOL, UNIFORMTYPE_INT, UNIFORMTYPE_FLOAT, UNIFORMTYPE_VEC2, UNIFORMTYPE_VEC3, - UNIFORMTYPE_VEC4, UNIFORMTYPE_IVEC2, UNIFORMTYPE_IVEC3, UNIFORMTYPE_IVEC4, UNIFORMTYPE_BVEC2, - UNIFORMTYPE_BVEC3, UNIFORMTYPE_BVEC4, UNIFORMTYPE_MAT4, UNIFORMTYPE_MAT2, UNIFORMTYPE_MAT3, - UNIFORMTYPE_FLOATARRAY, UNIFORMTYPE_VEC2ARRAY, UNIFORMTYPE_VEC3ARRAY, UNIFORMTYPE_VEC4ARRAY, - UNIFORMTYPE_MAT4ARRAY + UNIFORMTYPE_BOOL, UNIFORMTYPE_INT, UNIFORMTYPE_FLOAT, UNIFORMTYPE_UINT, UNIFORMTYPE_VEC2, + UNIFORMTYPE_VEC3, UNIFORMTYPE_VEC4, UNIFORMTYPE_IVEC2, UNIFORMTYPE_UVEC2, UNIFORMTYPE_IVEC3, + UNIFORMTYPE_IVEC4, UNIFORMTYPE_BVEC2, UNIFORMTYPE_BVEC3, UNIFORMTYPE_UVEC3, UNIFORMTYPE_BVEC4, + UNIFORMTYPE_MAT4, UNIFORMTYPE_MAT2, UNIFORMTYPE_MAT3, UNIFORMTYPE_FLOATARRAY, UNIFORMTYPE_UVEC4, + UNIFORMTYPE_VEC2ARRAY, UNIFORMTYPE_VEC3ARRAY, UNIFORMTYPE_VEC4ARRAY, UNIFORMTYPE_MAT4ARRAY, UNIFORMTYPE_INTARRAY, + UNIFORMTYPE_UINTARRAY, UNIFORMTYPE_BOOLARRAY, UNIFORMTYPE_IVEC2ARRAY, UNIFORMTYPE_UVEC2ARRAY, + UNIFORMTYPE_BVEC2ARRAY, UNIFORMTYPE_IVEC3ARRAY, UNIFORMTYPE_UVEC3ARRAY, UNIFORMTYPE_BVEC3ARRAY, + UNIFORMTYPE_IVEC4ARRAY, UNIFORMTYPE_UVEC4ARRAY, UNIFORMTYPE_BVEC4ARRAY } from './constants.js'; -// map of UNIFORMTYPE_*** to number of 32bit elements -const uniformTypeToNumElements = []; -uniformTypeToNumElements[UNIFORMTYPE_FLOAT] = 1; -uniformTypeToNumElements[UNIFORMTYPE_VEC2] = 2; -uniformTypeToNumElements[UNIFORMTYPE_VEC3] = 3; -uniformTypeToNumElements[UNIFORMTYPE_VEC4] = 4; -uniformTypeToNumElements[UNIFORMTYPE_INT] = 1; -uniformTypeToNumElements[UNIFORMTYPE_IVEC2] = 2; -uniformTypeToNumElements[UNIFORMTYPE_IVEC3] = 3; -uniformTypeToNumElements[UNIFORMTYPE_IVEC4] = 4; -uniformTypeToNumElements[UNIFORMTYPE_BOOL] = 1; -uniformTypeToNumElements[UNIFORMTYPE_BVEC2] = 2; -uniformTypeToNumElements[UNIFORMTYPE_BVEC3] = 3; -uniformTypeToNumElements[UNIFORMTYPE_BVEC4] = 4; -uniformTypeToNumElements[UNIFORMTYPE_MAT2] = 8; // 2 x vec4 -uniformTypeToNumElements[UNIFORMTYPE_MAT3] = 12; // 3 x vec4 -uniformTypeToNumElements[UNIFORMTYPE_MAT4] = 16; // 4 x vec4 +// map of UNIFORMTYPE_*** to number of 32bit components +const uniformTypeToNumComponents = []; +uniformTypeToNumComponents[UNIFORMTYPE_FLOAT] = 1; +uniformTypeToNumComponents[UNIFORMTYPE_INT] = 1; +uniformTypeToNumComponents[UNIFORMTYPE_UINT] = 1; +uniformTypeToNumComponents[UNIFORMTYPE_BOOL] = 1; + +uniformTypeToNumComponents[UNIFORMTYPE_VEC2] = 2; +uniformTypeToNumComponents[UNIFORMTYPE_IVEC2] = 2; +uniformTypeToNumComponents[UNIFORMTYPE_UVEC2] = 2; +uniformTypeToNumComponents[UNIFORMTYPE_BVEC2] = 2; + +uniformTypeToNumComponents[UNIFORMTYPE_VEC3] = 3; +uniformTypeToNumComponents[UNIFORMTYPE_IVEC3] = 3; +uniformTypeToNumComponents[UNIFORMTYPE_UVEC3] = 3; +uniformTypeToNumComponents[UNIFORMTYPE_BVEC3] = 3; + +uniformTypeToNumComponents[UNIFORMTYPE_VEC4] = 4; +uniformTypeToNumComponents[UNIFORMTYPE_IVEC4] = 4; +uniformTypeToNumComponents[UNIFORMTYPE_UVEC4] = 4; +uniformTypeToNumComponents[UNIFORMTYPE_BVEC4] = 4; + +uniformTypeToNumComponents[UNIFORMTYPE_MAT2] = 8; // 2 x vec4 +uniformTypeToNumComponents[UNIFORMTYPE_MAT3] = 12; // 3 x vec4 +uniformTypeToNumComponents[UNIFORMTYPE_MAT4] = 16; // 4 x vec4 /** * A class storing description of an individual uniform, stored inside a uniform buffer. @@ -60,6 +71,22 @@ class UniformFormat { */ count; + /** + * Number of components in each element (e.g. vec2 has 2 components, mat4 has 16 components) + * + * @type {number} + */ + numComponents; + + /** + * True if this is an array of elements (i.e. count > 0) + * + * @type {number} + */ + get isArrayType() { + return this.count > 0; + } + constructor(name, type, count = 0) { // just a name @@ -70,14 +97,33 @@ class UniformFormat { this.type = type; + this.numComponents = uniformTypeToNumComponents[type]; + Debug.assert(this.numComponents, `Unhandled uniform format ${type} used for ${name}`); + this.updateType = type; - if (count) { + if (count > 0) { switch (type) { case UNIFORMTYPE_FLOAT: this.updateType = UNIFORMTYPE_FLOATARRAY; break; + case UNIFORMTYPE_INT: this.updateType = UNIFORMTYPE_INTARRAY; break; + case UNIFORMTYPE_UINT: this.updateType = UNIFORMTYPE_UINTARRAY; break; + case UNIFORMTYPE_BOOL: this.updateType = UNIFORMTYPE_BOOLARRAY; break; + case UNIFORMTYPE_VEC2: this.updateType = UNIFORMTYPE_VEC2ARRAY; break; + case UNIFORMTYPE_IVEC2: this.updateType = UNIFORMTYPE_IVEC2ARRAY; break; + case UNIFORMTYPE_UVEC2: this.updateType = UNIFORMTYPE_UVEC2ARRAY; break; + case UNIFORMTYPE_BVEC2: this.updateType = UNIFORMTYPE_BVEC2ARRAY; break; + case UNIFORMTYPE_VEC3: this.updateType = UNIFORMTYPE_VEC3ARRAY; break; + case UNIFORMTYPE_IVEC3: this.updateType = UNIFORMTYPE_IVEC3ARRAY; break; + case UNIFORMTYPE_UVEC3: this.updateType = UNIFORMTYPE_UVEC3ARRAY; break; + case UNIFORMTYPE_BVEC3: this.updateType = UNIFORMTYPE_BVEC3ARRAY; break; + case UNIFORMTYPE_VEC4: this.updateType = UNIFORMTYPE_VEC4ARRAY; break; + case UNIFORMTYPE_IVEC4: this.updateType = UNIFORMTYPE_IVEC4ARRAY; break; + case UNIFORMTYPE_UVEC4: this.updateType = UNIFORMTYPE_UVEC4ARRAY; break; + case UNIFORMTYPE_BVEC4: this.updateType = UNIFORMTYPE_BVEC4ARRAY; break; + case UNIFORMTYPE_MAT4: this.updateType = UNIFORMTYPE_MAT4ARRAY; break; default: @@ -96,8 +142,7 @@ class UniformFormat { this.invalid = true; }); - let elementSize = uniformTypeToNumElements[type]; - Debug.assert(elementSize, `Unhandled uniform format ${type} used for ${name}`); + let elementSize = this.numElements; // element size for arrays is aligned up to vec4 if (count) diff --git a/src/platform/graphics/uniform-buffer.js b/src/platform/graphics/uniform-buffer.js index 798e7fa075c..5623d56c040 100644 --- a/src/platform/graphics/uniform-buffer.js +++ b/src/platform/graphics/uniform-buffer.js @@ -1,69 +1,33 @@ import { Debug } from '../../core/debug.js'; import { uniformTypeToName, - UNIFORMTYPE_INT, UNIFORMTYPE_FLOAT, UNIFORMTYPE_VEC2, UNIFORMTYPE_VEC3, + UNIFORMTYPE_INT, UNIFORMTYPE_FLOAT, UNIFORMTYPE_BOOL, UNIFORMTYPE_VEC2, + UNIFORMTYPE_VEC3, UNIFORMTYPE_UINT, UNIFORMTYPE_UVEC3, UNIFORMTYPE_UVEC4, UNIFORMTYPE_VEC4, UNIFORMTYPE_IVEC2, UNIFORMTYPE_IVEC3, UNIFORMTYPE_IVEC4, UNIFORMTYPE_FLOATARRAY, UNIFORMTYPE_VEC2ARRAY, UNIFORMTYPE_VEC3ARRAY, - UNIFORMTYPE_MAT2, UNIFORMTYPE_MAT3 + UNIFORMTYPE_MAT2, UNIFORMTYPE_MAT3, UNIFORMTYPE_UVEC2, UNIFORMTYPE_IVEC2ARRAY, UNIFORMTYPE_IVEC3ARRAY, UNIFORMTYPE_IVEC4ARRAY, UNIFORMTYPE_UVEC2ARRAY, UNIFORMTYPE_UVEC3ARRAY, UNIFORMTYPE_UVEC4ARRAY, UNIFORMTYPE_VEC4ARRAY, UNIFORMTYPE_BVEC2, UNIFORMTYPE_BVEC4, UNIFORMTYPE_BVEC3, UNIFORMTYPE_BVEC2ARRAY, UNIFORMTYPE_BVEC3ARRAY, UNIFORMTYPE_BVEC4ARRAY } from './constants.js'; import { DynamicBufferAllocation } from './dynamic-buffers.js'; +function updateSingleElement(uniformBuffer, value, offset, numComponents) { + const dst = uniformBuffer.getStorageForType(value.type); + for (let i = 0; i < numComponents; i++) { + dst[offset + i] = value[i]; + } +} + +function updateArray(uniformBuffer, value, offset, numComponents, count) { + const dst = uniformBuffer.getStorageForType(value.type); + for (let i = 0; i < count; i++) { + for (let j = 0; j < numComponents; j++) { + dst[offset + i * numComponents + j] = value[i * numComponents + j]; + } + } +} + // Uniform buffer set functions - only implemented for types for which the default // array to buffer copy does not work, or could be slower. const _updateFunctions = []; - -_updateFunctions[UNIFORMTYPE_FLOAT] = function (uniformBuffer, value, offset) { - const dst = uniformBuffer.storageFloat32; - dst[offset] = value; -}; - -_updateFunctions[UNIFORMTYPE_VEC2] = (uniformBuffer, value, offset) => { - const dst = uniformBuffer.storageFloat32; - dst[offset] = value[0]; - dst[offset + 1] = value[1]; -}; - -_updateFunctions[UNIFORMTYPE_VEC3] = (uniformBuffer, value, offset) => { - const dst = uniformBuffer.storageFloat32; - dst[offset] = value[0]; - dst[offset + 1] = value[1]; - dst[offset + 2] = value[2]; -}; - -_updateFunctions[UNIFORMTYPE_VEC4] = (uniformBuffer, value, offset) => { - const dst = uniformBuffer.storageFloat32; - dst[offset] = value[0]; - dst[offset + 1] = value[1]; - dst[offset + 2] = value[2]; - dst[offset + 3] = value[3]; -}; - -_updateFunctions[UNIFORMTYPE_INT] = function (uniformBuffer, value, offset) { - const dst = uniformBuffer.storageInt32; - dst[offset] = value; -}; - -_updateFunctions[UNIFORMTYPE_IVEC2] = function (uniformBuffer, value, offset) { - const dst = uniformBuffer.storageInt32; - dst[offset] = value[0]; - dst[offset + 1] = value[1]; -}; - -_updateFunctions[UNIFORMTYPE_IVEC3] = function (uniformBuffer, value, offset) { - const dst = uniformBuffer.storageInt32; - dst[offset] = value[0]; - dst[offset + 1] = value[1]; - dst[offset + 2] = value[2]; -}; - -_updateFunctions[UNIFORMTYPE_IVEC4] = function (uniformBuffer, value, offset) { - const dst = uniformBuffer.storageInt32; - dst[offset] = value[0]; - dst[offset + 1] = value[1]; - dst[offset + 2] = value[2]; - dst[offset + 3] = value[3]; -}; - // convert from continuous array to vec2[3] with padding to vec4[2] _updateFunctions[UNIFORMTYPE_MAT2] = (uniformBuffer, value, offset) => { const dst = uniformBuffer.storageFloat32; @@ -93,29 +57,6 @@ _updateFunctions[UNIFORMTYPE_MAT3] = (uniformBuffer, value, offset) => { dst[offset + 10] = value[8]; }; -_updateFunctions[UNIFORMTYPE_FLOATARRAY] = function (uniformBuffer, value, offset, count) { - const dst = uniformBuffer.storageFloat32; - for (let i = 0; i < count; i++) { - dst[offset + i * 4] = value[i]; - } -}; - -_updateFunctions[UNIFORMTYPE_VEC2ARRAY] = (uniformBuffer, value, offset, count) => { - const dst = uniformBuffer.storageFloat32; - for (let i = 0; i < count; i++) { - dst[offset + i * 4] = value[i * 2]; - dst[offset + i * 4 + 1] = value[i * 2 + 1]; - } -}; - -_updateFunctions[UNIFORMTYPE_VEC3ARRAY] = (uniformBuffer, value, offset, count) => { - const dst = uniformBuffer.storageFloat32; - for (let i = 0; i < count; i++) { - dst[offset + i * 4] = value[i * 3]; - dst[offset + i * 4 + 1] = value[i * 3 + 1]; - dst[offset + i * 4 + 2] = value[i * 3 + 2]; - } -}; /** * A uniform buffer represents a GPU memory buffer storing the uniforms. @@ -137,6 +78,9 @@ class UniformBuffer { /** @type {Int32Array} */ storageInt32; + /** @type {Uint32Array} */ + storageUint32; + /** * A render version used to track the last time the properties requiring bind group to be * updated were changed. @@ -204,6 +148,7 @@ class UniformBuffer { */ assignStorage(storage) { this.storageInt32 = storage; + this.storageUint32 = new Uint32Array(storage.buffer, storage.byteOffset, storage.byteLength / 4); this.storageFloat32 = new Float32Array(storage.buffer, storage.byteOffset, storage.byteLength / 4); } @@ -232,9 +177,13 @@ class UniformBuffer { const updateFunction = _updateFunctions[uniformFormat.updateType]; if (updateFunction) { - updateFunction(this, value, offset, uniformFormat.count); + updateFunction(this, value, offset, uniformFormat.numComponents, uniformFormat.count); } else { - this.storageFloat32.set(value, offset); + if (uniformFormat.isArrayType) { + updateArray(this, value, offset, uniformFormat.numComponents, uniformFormat.count); + } else { + updateSingleElement(this, value, offset, uniformFormat.numComponents); + } } } else { Debug.warnOnce(`Value was not set when assigning to uniform [${uniformFormat.name}]` + @@ -284,6 +233,46 @@ class UniformBuffer { } else { this.storageFloat32 = null; this.storageInt32 = null; + this.storageUint32 = null; + } + } + + getStorageForType(type) { + switch (type) { + case UNIFORMTYPE_INT: + case UNIFORMTYPE_BOOL: + case UNIFORMTYPE_IVEC2: + case UNIFORMTYPE_IVEC3: + case UNIFORMTYPE_IVEC4: + case UNIFORMTYPE_BVEC2: + case UNIFORMTYPE_BVEC3: + case UNIFORMTYPE_BVEC4: + case UNIFORMTYPE_IVEC2ARRAY: + case UNIFORMTYPE_IVEC3ARRAY: + case UNIFORMTYPE_IVEC4ARRAY: + case UNIFORMTYPE_BVEC2ARRAY: + case UNIFORMTYPE_BVEC3ARRAY: + case UNIFORMTYPE_BVEC4ARRAY: + return this.storageInt32; + case UNIFORMTYPE_UINT: + case UNIFORMTYPE_UVEC2: + case UNIFORMTYPE_UVEC3: + case UNIFORMTYPE_UVEC4: + case UNIFORMTYPE_UVEC2ARRAY: + case UNIFORMTYPE_UVEC3ARRAY: + case UNIFORMTYPE_UVEC4ARRAY: + return this.storageUint32; + case UNIFORMTYPE_FLOAT: + case UNIFORMTYPE_VEC2: + case UNIFORMTYPE_VEC3: + case UNIFORMTYPE_VEC4: + case UNIFORMTYPE_MAT2: + case UNIFORMTYPE_MAT3: + case UNIFORMTYPE_FLOATARRAY: + case UNIFORMTYPE_VEC2ARRAY: + case UNIFORMTYPE_VEC3ARRAY: + case UNIFORMTYPE_VEC4ARRAY: + return this.storageFloat32; } } } diff --git a/src/platform/graphics/webgl/webgl-graphics-device.js b/src/platform/graphics/webgl/webgl-graphics-device.js index 4848ea8d9fa..e9ab3ac61cf 100644 --- a/src/platform/graphics/webgl/webgl-graphics-device.js +++ b/src/platform/graphics/webgl/webgl-graphics-device.js @@ -18,11 +18,16 @@ import { UNIFORMTYPE_BVEC3, UNIFORMTYPE_BVEC4, UNIFORMTYPE_MAT2, UNIFORMTYPE_MAT3, UNIFORMTYPE_MAT4, UNIFORMTYPE_TEXTURE2D, UNIFORMTYPE_TEXTURECUBE, UNIFORMTYPE_FLOATARRAY, UNIFORMTYPE_TEXTURE2D_SHADOW, UNIFORMTYPE_TEXTURECUBE_SHADOW, UNIFORMTYPE_TEXTURE3D, UNIFORMTYPE_VEC2ARRAY, UNIFORMTYPE_VEC3ARRAY, UNIFORMTYPE_VEC4ARRAY, + UNIFORMTYPE_UINT, UNIFORMTYPE_UVEC2, UNIFORMTYPE_UVEC3, UNIFORMTYPE_UVEC4, UNIFORMTYPE_ITEXTURE2D, UNIFORMTYPE_UTEXTURE2D, + UNIFORMTYPE_ITEXTURECUBE, UNIFORMTYPE_UTEXTURECUBE, UNIFORMTYPE_ITEXTURE3D, UNIFORMTYPE_UTEXTURE3D, UNIFORMTYPE_ITEXTURE2D_ARRAY, + UNIFORMTYPE_UTEXTURE2D_ARRAY, UNIFORMTYPE_INTARRAY, UNIFORMTYPE_UINTARRAY, UNIFORMTYPE_BOOLARRAY, UNIFORMTYPE_IVEC2ARRAY, + UNIFORMTYPE_BVEC2ARRAY, UNIFORMTYPE_UVEC2ARRAY, UNIFORMTYPE_IVEC3ARRAY, UNIFORMTYPE_BVEC3ARRAY, UNIFORMTYPE_UVEC3ARRAY, + UNIFORMTYPE_IVEC4ARRAY, UNIFORMTYPE_BVEC4ARRAY, UNIFORMTYPE_UVEC4ARRAY, UNIFORMTYPE_TEXTURE2D_ARRAY, semanticToLocation, - UNIFORMTYPE_TEXTURE2D_ARRAY, PRIMITIVE_TRISTRIP, DEVICETYPE_WEBGL2, - DEVICETYPE_WEBGL1 + DEVICETYPE_WEBGL1, + UNIFORMTYPE_MAT4ARRAY } from '../constants.js'; import { GraphicsDevice } from '../graphics-device.js'; @@ -545,28 +550,48 @@ class WebglGraphicsDevice extends GraphicsDevice { ]; this.pcUniformType = {}; - this.pcUniformType[gl.BOOL] = UNIFORMTYPE_BOOL; - this.pcUniformType[gl.INT] = UNIFORMTYPE_INT; - this.pcUniformType[gl.FLOAT] = UNIFORMTYPE_FLOAT; - this.pcUniformType[gl.FLOAT_VEC2] = UNIFORMTYPE_VEC2; - this.pcUniformType[gl.FLOAT_VEC3] = UNIFORMTYPE_VEC3; - this.pcUniformType[gl.FLOAT_VEC4] = UNIFORMTYPE_VEC4; - this.pcUniformType[gl.INT_VEC2] = UNIFORMTYPE_IVEC2; - this.pcUniformType[gl.INT_VEC3] = UNIFORMTYPE_IVEC3; - this.pcUniformType[gl.INT_VEC4] = UNIFORMTYPE_IVEC4; - this.pcUniformType[gl.BOOL_VEC2] = UNIFORMTYPE_BVEC2; - this.pcUniformType[gl.BOOL_VEC3] = UNIFORMTYPE_BVEC3; - this.pcUniformType[gl.BOOL_VEC4] = UNIFORMTYPE_BVEC4; - this.pcUniformType[gl.FLOAT_MAT2] = UNIFORMTYPE_MAT2; - this.pcUniformType[gl.FLOAT_MAT3] = UNIFORMTYPE_MAT3; - this.pcUniformType[gl.FLOAT_MAT4] = UNIFORMTYPE_MAT4; - this.pcUniformType[gl.SAMPLER_2D] = UNIFORMTYPE_TEXTURE2D; - this.pcUniformType[gl.SAMPLER_CUBE] = UNIFORMTYPE_TEXTURECUBE; + this.pcUniformType[gl.FLOAT] = UNIFORMTYPE_FLOAT; + this.pcUniformType[gl.INT] = UNIFORMTYPE_INT; + this.pcUniformType[gl.UNSIGNED_INT] = UNIFORMTYPE_UINT; + this.pcUniformType[gl.BOOL] = UNIFORMTYPE_BOOL; + + this.pcUniformType[gl.FLOAT_VEC2] = UNIFORMTYPE_VEC2; + this.pcUniformType[gl.INT_VEC2] = UNIFORMTYPE_IVEC2; + this.pcUniformType[gl.UNSIGNED_INT_VEC2] = UNIFORMTYPE_UVEC2; + this.pcUniformType[gl.BOOL_VEC2] = UNIFORMTYPE_BVEC2; + + this.pcUniformType[gl.FLOAT_VEC3] = UNIFORMTYPE_VEC3; + this.pcUniformType[gl.INT_VEC3] = UNIFORMTYPE_IVEC3; + this.pcUniformType[gl.UNSIGNED_INT_VEC3] = UNIFORMTYPE_UVEC3; + this.pcUniformType[gl.BOOL_VEC3] = UNIFORMTYPE_BVEC3; + + this.pcUniformType[gl.FLOAT_VEC4] = UNIFORMTYPE_VEC4; + this.pcUniformType[gl.INT_VEC4] = UNIFORMTYPE_IVEC4; + this.pcUniformType[gl.UNSIGNED_INT_VEC4] = UNIFORMTYPE_UVEC4; + this.pcUniformType[gl.BOOL_VEC4] = UNIFORMTYPE_BVEC4; + + this.pcUniformType[gl.FLOAT_MAT2] = UNIFORMTYPE_MAT2; + this.pcUniformType[gl.FLOAT_MAT3] = UNIFORMTYPE_MAT3; + this.pcUniformType[gl.FLOAT_MAT4] = UNIFORMTYPE_MAT4; + this.pcUniformType[gl.SAMPLER_2D] = UNIFORMTYPE_TEXTURE2D; + this.pcUniformType[gl.SAMPLER_CUBE] = UNIFORMTYPE_TEXTURECUBE; + if (this.isWebGL2) { - this.pcUniformType[gl.SAMPLER_2D_SHADOW] = UNIFORMTYPE_TEXTURE2D_SHADOW; - this.pcUniformType[gl.SAMPLER_CUBE_SHADOW] = UNIFORMTYPE_TEXTURECUBE_SHADOW; - this.pcUniformType[gl.SAMPLER_2D_ARRAY] = UNIFORMTYPE_TEXTURE2D_ARRAY; - this.pcUniformType[gl.SAMPLER_3D] = UNIFORMTYPE_TEXTURE3D; + this.pcUniformType[gl.INT_SAMPLER_2D] = UNIFORMTYPE_ITEXTURE2D; + this.pcUniformType[gl.UNSIGNED_INT_SAMPLER_2D] = UNIFORMTYPE_UTEXTURE2D; + this.pcUniformType[gl.SAMPLER_2D_SHADOW] = UNIFORMTYPE_TEXTURE2D_SHADOW; + + this.pcUniformType[gl.INT_SAMPLER_CUBE] = UNIFORMTYPE_ITEXTURECUBE; + this.pcUniformType[gl.UNSIGNED_INT_SAMPLER_2D] = UNIFORMTYPE_UTEXTURECUBE; + this.pcUniformType[gl.SAMPLER_CUBE_SHADOW] = UNIFORMTYPE_TEXTURECUBE_SHADOW; + + this.pcUniformType[gl.SAMPLER_3D] = UNIFORMTYPE_TEXTURE3D; + this.pcUniformType[gl.INT_SAMPLER_3D] = UNIFORMTYPE_ITEXTURE3D; + this.pcUniformType[gl.UNSIGNED_INT_SAMPLER_3D] = UNIFORMTYPE_UTEXTURE3D; + + this.pcUniformType[gl.SAMPLER_2D_ARRAY] = UNIFORMTYPE_TEXTURE2D_ARRAY; + this.pcUniformType[gl.INT_SAMPLER_2D_ARRAY] = UNIFORMTYPE_ITEXTURE2D_ARRAY; + this.pcUniformType[gl.UNSIGNED_INT_SAMPLER_2D_ARRAY] = UNIFORMTYPE_UTEXTURE2D_ARRAY; } this.targetToSlot = {}; @@ -578,19 +603,26 @@ class WebglGraphicsDevice extends GraphicsDevice { let scopeX, scopeY, scopeZ, scopeW; let uniformValue; this.commitFunction = []; - this.commitFunction[UNIFORMTYPE_BOOL] = function (uniform, value) { + this.commitFunction[UNIFORMTYPE_FLOAT] = function (uniform, value) { + if (uniform.value !== value) { + gl.uniform1f(uniform.locationId, value); + uniform.value = value; + } + }; + this.commitFunction[UNIFORMTYPE_INT] = function (uniform, value) { if (uniform.value !== value) { gl.uniform1i(uniform.locationId, value); uniform.value = value; } }; - this.commitFunction[UNIFORMTYPE_INT] = this.commitFunction[UNIFORMTYPE_BOOL]; - this.commitFunction[UNIFORMTYPE_FLOAT] = function (uniform, value) { + this.commitFunction[UNIFORMTYPE_UINT] = function (uniform, value) { if (uniform.value !== value) { - gl.uniform1f(uniform.locationId, value); + gl.uniform1ui(uniform.locationId, value); uniform.value = value; } }; + this.commitFunction[UNIFORMTYPE_BOOL] = this.commitFunction[UNIFORMTYPE_INT]; + this.commitFunction[UNIFORMTYPE_VEC2] = function (uniform, value) { uniformValue = uniform.value; scopeX = value[0]; @@ -601,6 +633,28 @@ class WebglGraphicsDevice extends GraphicsDevice { uniformValue[1] = scopeY; } }; + this.commitFunction[UNIFORMTYPE_IVEC2] = function (uniform, value) { + uniformValue = uniform.value; + scopeX = value[0]; + scopeY = value[1]; + if (uniformValue[0] !== scopeX || uniformValue[1] !== scopeY) { + gl.uniform2iv(uniform.locationId, value); + uniformValue[0] = scopeX; + uniformValue[1] = scopeY; + } + }; + this.commitFunction[UNIFORMTYPE_UVEC2] = function (uniform, value) { + uniformValue = uniform.value; + scopeX = value[0]; + scopeY = value[1]; + if (uniformValue[0] !== scopeX || uniformValue[1] !== scopeY) { + gl.uniform2uiv(uniform.locationId, value); + uniformValue[0] = scopeX; + uniformValue[1] = scopeY; + } + }; + this.commitFunction[UNIFORMTYPE_BVEC2] = this.commitFunction[UNIFORMTYPE_IVEC2]; + this.commitFunction[UNIFORMTYPE_VEC3] = function (uniform, value) { uniformValue = uniform.value; scopeX = value[0]; @@ -613,44 +667,46 @@ class WebglGraphicsDevice extends GraphicsDevice { uniformValue[2] = scopeZ; } }; - this.commitFunction[UNIFORMTYPE_VEC4] = function (uniform, value) { + this.commitFunction[UNIFORMTYPE_IVEC3] = function (uniform, value) { uniformValue = uniform.value; scopeX = value[0]; scopeY = value[1]; scopeZ = value[2]; - scopeW = value[3]; - if (uniformValue[0] !== scopeX || uniformValue[1] !== scopeY || uniformValue[2] !== scopeZ || uniformValue[3] !== scopeW) { - gl.uniform4fv(uniform.locationId, value); + if (uniformValue[0] !== scopeX || uniformValue[1] !== scopeY || uniformValue[2] !== scopeZ) { + gl.uniform3iv(uniform.locationId, value); uniformValue[0] = scopeX; uniformValue[1] = scopeY; uniformValue[2] = scopeZ; - uniformValue[3] = scopeW; } }; - this.commitFunction[UNIFORMTYPE_IVEC2] = function (uniform, value) { + this.commitFunction[UNIFORMTYPE_UVEC3] = function (uniform, value) { uniformValue = uniform.value; scopeX = value[0]; scopeY = value[1]; - if (uniformValue[0] !== scopeX || uniformValue[1] !== scopeY) { - gl.uniform2iv(uniform.locationId, value); + scopeZ = value[2]; + if (uniformValue[0] !== scopeX || uniformValue[1] !== scopeY || uniformValue[2] !== scopeZ) { + gl.uniform3uiv(uniform.locationId, value); uniformValue[0] = scopeX; uniformValue[1] = scopeY; + uniformValue[2] = scopeZ; } }; - this.commitFunction[UNIFORMTYPE_BVEC2] = this.commitFunction[UNIFORMTYPE_IVEC2]; - this.commitFunction[UNIFORMTYPE_IVEC3] = function (uniform, value) { + this.commitFunction[UNIFORMTYPE_BVEC3] = this.commitFunction[UNIFORMTYPE_IVEC3]; + + this.commitFunction[UNIFORMTYPE_VEC4] = function (uniform, value) { uniformValue = uniform.value; scopeX = value[0]; scopeY = value[1]; scopeZ = value[2]; - if (uniformValue[0] !== scopeX || uniformValue[1] !== scopeY || uniformValue[2] !== scopeZ) { - gl.uniform3iv(uniform.locationId, value); + scopeW = value[3]; + if (uniformValue[0] !== scopeX || uniformValue[1] !== scopeY || uniformValue[2] !== scopeZ || uniformValue[3] !== scopeW) { + gl.uniform4fv(uniform.locationId, value); uniformValue[0] = scopeX; uniformValue[1] = scopeY; uniformValue[2] = scopeZ; + uniformValue[3] = scopeW; } }; - this.commitFunction[UNIFORMTYPE_BVEC3] = this.commitFunction[UNIFORMTYPE_IVEC3]; this.commitFunction[UNIFORMTYPE_IVEC4] = function (uniform, value) { uniformValue = uniform.value; scopeX = value[0]; @@ -665,7 +721,22 @@ class WebglGraphicsDevice extends GraphicsDevice { uniformValue[3] = scopeW; } }; + this.commitFunction[UNIFORMTYPE_UVEC4] = function (uniform, value) { + uniformValue = uniform.value; + scopeX = value[0]; + scopeY = value[1]; + scopeZ = value[2]; + scopeW = value[3]; + if (uniformValue[0] !== scopeX || uniformValue[1] !== scopeY || uniformValue[2] !== scopeZ || uniformValue[3] !== scopeW) { + gl.uniform4uiv(uniform.locationId, value); + uniformValue[0] = scopeX; + uniformValue[1] = scopeY; + uniformValue[2] = scopeZ; + uniformValue[3] = scopeW; + } + }; this.commitFunction[UNIFORMTYPE_BVEC4] = this.commitFunction[UNIFORMTYPE_IVEC4]; + this.commitFunction[UNIFORMTYPE_MAT2] = function (uniform, value) { gl.uniformMatrix2fv(uniform.locationId, false, value); }; @@ -675,18 +746,54 @@ class WebglGraphicsDevice extends GraphicsDevice { this.commitFunction[UNIFORMTYPE_MAT4] = function (uniform, value) { gl.uniformMatrix4fv(uniform.locationId, false, value); }; + this.commitFunction[UNIFORMTYPE_FLOATARRAY] = function (uniform, value) { gl.uniform1fv(uniform.locationId, value); }; + this.commitFunction[UNIFORMTYPE_INTARRAY] = function (uniform, value) { + gl.uniform1iv(uniform.locationId, value); + }; + this.commitFunction[UNIFORMTYPE_UINTARRAY] = function (uniform, value) { + gl.uniform1uiv(uniform.locationId, value); + }; + this.commitFunction[UNIFORMTYPE_BOOLARRAY] = this.commitFunction[UNIFORMTYPE_INTARRAY]; + this.commitFunction[UNIFORMTYPE_VEC2ARRAY] = function (uniform, value) { gl.uniform2fv(uniform.locationId, value); }; + this.commitFunction[UNIFORMTYPE_IVEC2ARRAY] = function (uniform, value) { + gl.uniform2iv(uniform.locationId, value); + }; + this.commitFunction[UNIFORMTYPE_UVEC2ARRAY] = function (uniform, value) { + gl.uniform2uiv(uniform.locationId, value); + }; + this.commitFunction[UNIFORMTYPE_BVEC2ARRAY] = this.commitFunction[UNIFORMTYPE_IVEC2ARRAY]; + this.commitFunction[UNIFORMTYPE_VEC3ARRAY] = function (uniform, value) { gl.uniform3fv(uniform.locationId, value); }; + this.commitFunction[UNIFORMTYPE_IVEC3ARRAY] = function (uniform, value) { + gl.uniform3iv(uniform.locationId, value); + }; + this.commitFunction[UNIFORMTYPE_UVEC3ARRAY] = function (uniform, value) { + gl.uniform3uiv(uniform.locationId, value); + }; + this.commitFunction[UNIFORMTYPE_BVEC3ARRAY] = this.commitFunction[UNIFORMTYPE_IVEC2ARRAY]; + this.commitFunction[UNIFORMTYPE_VEC4ARRAY] = function (uniform, value) { gl.uniform4fv(uniform.locationId, value); }; + this.commitFunction[UNIFORMTYPE_IVEC4ARRAY] = function (uniform, value) { + gl.uniform4iv(uniform.locationId, value); + }; + this.commitFunction[UNIFORMTYPE_UVEC4ARRAY] = function (uniform, value) { + gl.uniform4uiv(uniform.locationId, value); + }; + this.commitFunction[UNIFORMTYPE_BVEC4ARRAY] = this.commitFunction[UNIFORMTYPE_IVEC2ARRAY]; + + this.commitFunction[UNIFORMTYPE_MAT4ARRAY] = function (uniform, value) { + gl.uniformMatrix4fv(uniform.locationId, false, value); + }; this.supportsBoneTextures = this.extTextureFloat && this.maxVertexTextures > 0; diff --git a/src/platform/graphics/webgl/webgl-shader-input.js b/src/platform/graphics/webgl/webgl-shader-input.js index 2dcbf4be691..f78673b5bc6 100644 --- a/src/platform/graphics/webgl/webgl-shader-input.js +++ b/src/platform/graphics/webgl/webgl-shader-input.js @@ -1,5 +1,5 @@ import { UNIFORMTYPE_FLOAT, UNIFORMTYPE_FLOATARRAY, UNIFORMTYPE_VEC2, UNIFORMTYPE_VEC3, UNIFORMTYPE_VEC4, - UNIFORMTYPE_VEC2ARRAY, UNIFORMTYPE_VEC3ARRAY, UNIFORMTYPE_VEC4ARRAY } from '../constants.js'; + UNIFORMTYPE_VEC2ARRAY, UNIFORMTYPE_VEC3ARRAY, UNIFORMTYPE_VEC4ARRAY, UNIFORMTYPE_INT, UNIFORMTYPE_INTARRAY, UNIFORMTYPE_UINT, UNIFORMTYPE_UINTARRAY, UNIFORMTYPE_BOOL, UNIFORMTYPE_BOOLARRAY, UNIFORMTYPE_IVEC2, UNIFORMTYPE_IVEC2ARRAY, UNIFORMTYPE_UVEC2, UNIFORMTYPE_UVEC2ARRAY, UNIFORMTYPE_BVEC2, UNIFORMTYPE_BVEC2ARRAY, UNIFORMTYPE_IVEC3, UNIFORMTYPE_UVEC3, UNIFORMTYPE_BVEC3, UNIFORMTYPE_IVEC4, UNIFORMTYPE_UVEC4, UNIFORMTYPE_BVEC4, UNIFORMTYPE_IVEC3ARRAY, UNIFORMTYPE_UVEC3ARRAY, UNIFORMTYPE_BVEC3ARRAY, UNIFORMTYPE_IVEC4ARRAY, UNIFORMTYPE_UVEC4ARRAY, UNIFORMTYPE_BVEC4ARRAY } from '../constants.js'; import { Version } from '../version.js'; /** @@ -31,9 +31,24 @@ class WebglShaderInput { if (name.substring(name.length - 3) === "[0]") { switch (type) { case UNIFORMTYPE_FLOAT: type = UNIFORMTYPE_FLOATARRAY; break; + case UNIFORMTYPE_INT: type = UNIFORMTYPE_INTARRAY; break; + case UNIFORMTYPE_UINT: type = UNIFORMTYPE_UINTARRAY; break; + case UNIFORMTYPE_BOOL: type = UNIFORMTYPE_BOOLARRAY; break; + case UNIFORMTYPE_VEC2: type = UNIFORMTYPE_VEC2ARRAY; break; + case UNIFORMTYPE_IVEC2: type = UNIFORMTYPE_IVEC2ARRAY; break; + case UNIFORMTYPE_UVEC2: type = UNIFORMTYPE_UVEC2ARRAY; break; + case UNIFORMTYPE_BVEC2: type = UNIFORMTYPE_BVEC2ARRAY; break; + case UNIFORMTYPE_VEC3: type = UNIFORMTYPE_VEC3ARRAY; break; + case UNIFORMTYPE_IVEC3: type = UNIFORMTYPE_IVEC3ARRAY; break; + case UNIFORMTYPE_UVEC3: type = UNIFORMTYPE_UVEC3ARRAY; break; + case UNIFORMTYPE_BVEC3: type = UNIFORMTYPE_BVEC3ARRAY; break; + case UNIFORMTYPE_VEC4: type = UNIFORMTYPE_VEC4ARRAY; break; + case UNIFORMTYPE_IVEC4: type = UNIFORMTYPE_IVEC4ARRAY; break; + case UNIFORMTYPE_UVEC4: type = UNIFORMTYPE_UVEC4ARRAY; break; + case UNIFORMTYPE_BVEC4: type = UNIFORMTYPE_BVEC4ARRAY; break; } } diff --git a/src/platform/graphics/webgl/webgl-shader.js b/src/platform/graphics/webgl/webgl-shader.js index c743c71496c..36bdd21d86f 100644 --- a/src/platform/graphics/webgl/webgl-shader.js +++ b/src/platform/graphics/webgl/webgl-shader.js @@ -361,9 +361,23 @@ class WebglShader { const shaderInput = new WebglShaderInput(device, info.name, device.pcUniformType[info.type], location); - if (info.type === gl.SAMPLER_2D || info.type === gl.SAMPLER_CUBE || - (device.isWebGL2 && (info.type === gl.SAMPLER_2D_SHADOW || info.type === gl.SAMPLER_CUBE_SHADOW || - info.type === gl.SAMPLER_3D || info.type === gl.SAMPLER_2D_ARRAY)) + if ( + info.type === gl.SAMPLER_2D || + info.type === gl.SAMPLER_CUBE || + ( + device.isWebGL2 && ( + info.type === gl.UNSIGNED_INT_SAMPLER_2D || + info.type === gl.INT_SAMPLER_2D || + info.type === gl.SAMPLER_2D_SHADOW || + info.type === gl.SAMPLER_CUBE_SHADOW || + info.type === gl.SAMPLER_3D || + info.type === gl.INT_SAMPLER_3D || + info.type === gl.UNSIGNED_INT_SAMPLER_3D || + info.type === gl.SAMPLER_2D_ARRAY || + info.type === gl.INT_SAMPLER_2D_ARRAY || + info.type === gl.UNSIGNED_INT_SAMPLER_2D_ARRAY + ) + ) ) { this.samplers.push(shaderInput); } else { diff --git a/src/platform/graphics/webgl/webgl-texture.js b/src/platform/graphics/webgl/webgl-texture.js index 29016b06023..6ab11a5e399 100644 --- a/src/platform/graphics/webgl/webgl-texture.js +++ b/src/platform/graphics/webgl/webgl-texture.js @@ -7,7 +7,10 @@ import { PIXELFORMAT_DEPTHSTENCIL, PIXELFORMAT_111110F, PIXELFORMAT_SRGB, PIXELFORMAT_SRGBA, PIXELFORMAT_ETC1, PIXELFORMAT_ETC2_RGB, PIXELFORMAT_ETC2_RGBA, PIXELFORMAT_PVRTC_2BPP_RGB_1, PIXELFORMAT_PVRTC_2BPP_RGBA_1, PIXELFORMAT_PVRTC_4BPP_RGB_1, PIXELFORMAT_PVRTC_4BPP_RGBA_1, PIXELFORMAT_ASTC_4x4, PIXELFORMAT_ATC_RGB, - PIXELFORMAT_ATC_RGBA, PIXELFORMAT_BGRA8 + PIXELFORMAT_ATC_RGBA, PIXELFORMAT_BGRA8, PIXELFORMAT_R8I, PIXELFORMAT_R8UI, PIXELFORMAT_R16I, PIXELFORMAT_R16UI, + PIXELFORMAT_R32I, PIXELFORMAT_R32UI, PIXELFORMAT_RG16I, PIXELFORMAT_RG16UI, PIXELFORMAT_RG32I, PIXELFORMAT_RG32UI, + PIXELFORMAT_RG8I, PIXELFORMAT_RG8UI, PIXELFORMAT_RGBA16I, PIXELFORMAT_RGBA16UI, PIXELFORMAT_RGBA32I, PIXELFORMAT_RGBA32UI, + PIXELFORMAT_RGBA8I, PIXELFORMAT_RGBA8UI } from '../constants.js'; /** @@ -279,6 +282,99 @@ class WebglTexture { this._glInternalFormat = gl.SRGB8_ALPHA8; this._glPixelType = gl.UNSIGNED_BYTE; break; + // Integer texture formats (R) (WebGL2 only) + case PIXELFORMAT_R8I: // WebGL2 only + this._glFormat = gl.RED_INTEGER; + this._glInternalFormat = gl.R8I; + this._glPixelType = gl.BYTE; + break; + case PIXELFORMAT_R8UI: // WebGL2 only + this._glFormat = gl.RED_INTEGER; + this._glInternalFormat = gl.R8UI; + this._glPixelType = gl.UNSIGNED_BYTE; + break; + case PIXELFORMAT_R16I: // WebGL2 only + this._glFormat = gl.RED_INTEGER; + this._glInternalFormat = gl.R16I; + this._glPixelType = gl.SHORT; + break; + case PIXELFORMAT_R16UI: // WebGL2 only + this._glFormat = gl.RED_INTEGER; + this._glInternalFormat = gl.R16UI; + this._glPixelType = gl.UNSIGNED_SHORT; + break; + case PIXELFORMAT_R32I: // WebGL2 only + this._glFormat = gl.RED_INTEGER; + this._glInternalFormat = gl.R32I; + this._glPixelType = gl.INT; + break; + case PIXELFORMAT_R32UI: // WebGL2 only + this._glFormat = gl.RED_INTEGER; + this._glInternalFormat = gl.R32UI; + this._glPixelType = gl.UNSIGNED_INT; + break; + // Integer texture formats (RG) (WebGL2 only) + case PIXELFORMAT_RG8I: // WebGL2 only + this._glFormat = gl.RG_INTEGER; + this._glInternalFormat = gl.RG8I; + this._glPixelType = gl.BYTE; + break; + case PIXELFORMAT_RG8UI: // WebGL2 only + this._glFormat = gl.RG_INTEGER; + this._glInternalFormat = gl.RG8UI; + this._glPixelType = gl.UNSIGNED_BYTE; + break; + case PIXELFORMAT_RG16I: // WebGL2 only + this._glFormat = gl.RG_INTEGER; + this._glInternalFormat = gl.RG16I; + this._glPixelType = gl.SHORT; + break; + case PIXELFORMAT_RG16UI: // WebGL2 only + this._glFormat = gl.RG_INTEGER; + this._glInternalFormat = gl.RG16UI; + this._glPixelType = gl.UNSIGNED_SHORT; + break; + case PIXELFORMAT_RG32I: // WebGL2 only + this._glFormat = gl.RG_INTEGER; + this._glInternalFormat = gl.RG32I; + this._glPixelType = gl.INT; + break; + case PIXELFORMAT_RG32UI: // WebGL2 only + this._glFormat = gl.RG_INTEGER; + this._glInternalFormat = gl.RG32UI; + this._glPixelType = gl.UNSIGNED_INT; + break; + // Integer texture formats (RGBA) (WebGL2 only) + case PIXELFORMAT_RGBA8I: // WebGL2 only + this._glFormat = gl.RGBA_INTEGER; + this._glInternalFormat = gl.RGBA8I; + this._glPixelType = gl.BYTE; + break; + case PIXELFORMAT_RGBA8UI: // WebGL2 only + this._glFormat = gl.RGBA_INTEGER; + this._glInternalFormat = gl.RGBA8UI; + this._glPixelType = gl.UNSIGNED_BYTE; + break; + case PIXELFORMAT_RGBA16I: // WebGL2 only + this._glFormat = gl.RGBA_INTEGER; + this._glInternalFormat = gl.RGBA16I; + this._glPixelType = gl.SHORT; + break; + case PIXELFORMAT_RGBA16UI: // WebGL2 only + this._glFormat = gl.RGBA_INTEGER; + this._glInternalFormat = gl.RGBA16UI; + this._glPixelType = gl.UNSIGNED_SHORT; + break; + case PIXELFORMAT_RGBA32I: // WebGL2 only + this._glFormat = gl.RGBA_INTEGER; + this._glInternalFormat = gl.RGBA32I; + this._glPixelType = gl.INT; + break; + case PIXELFORMAT_RGBA32UI: // WebGL2 only + this._glFormat = gl.RGBA_INTEGER; + this._glInternalFormat = gl.RGBA32UI; + this._glPixelType = gl.UNSIGNED_INT; + break; case PIXELFORMAT_BGRA8: Debug.error("BGRA8 texture format is not supported by WebGL."); break; @@ -324,7 +420,7 @@ class WebglTexture { mipObject = texture._levels[mipLevel]; resMult = 1 / Math.pow(2, mipLevel); - if (mipLevel === 1 && !texture._compressed && texture._levels.length < requiredMipLevels) { + if (mipLevel === 1 && !texture._compressed && !texture._integerFormat && texture._levels.length < requiredMipLevels) { // We have more than one mip levels we want to assign, but we need all mips to make // the texture complete. Therefore first generate all mip chain from 0, then assign custom mips. // (this implies the call to _completePartialMipLevels above was unsuccessful) @@ -618,7 +714,7 @@ class WebglTexture { } } - if (!texture._compressed && texture._mipmaps && texture._needsMipmapsUpload && (texture.pot || device.isWebGL2) && texture._levels.length === 1) { + if (!texture._compressed && !texture._integerFormat && texture._mipmaps && texture._needsMipmapsUpload && (texture.pot || device.isWebGL2) && texture._levels.length === 1) { gl.generateMipmap(this._glTarget); texture._mipmapsUploaded = true; } diff --git a/src/platform/graphics/webgpu/constants.js b/src/platform/graphics/webgpu/constants.js index be49739ceec..8a2c3f9db98 100644 --- a/src/platform/graphics/webgpu/constants.js +++ b/src/platform/graphics/webgpu/constants.js @@ -5,7 +5,10 @@ import { PIXELFORMAT_DEPTHSTENCIL, PIXELFORMAT_111110F, PIXELFORMAT_SRGB, PIXELFORMAT_SRGBA, PIXELFORMAT_ETC1, PIXELFORMAT_ETC2_RGB, PIXELFORMAT_ETC2_RGBA, PIXELFORMAT_PVRTC_2BPP_RGB_1, PIXELFORMAT_PVRTC_2BPP_RGBA_1, PIXELFORMAT_PVRTC_4BPP_RGB_1, PIXELFORMAT_PVRTC_4BPP_RGBA_1, PIXELFORMAT_ASTC_4x4, PIXELFORMAT_ATC_RGB, - PIXELFORMAT_ATC_RGBA, PIXELFORMAT_BGRA8 + PIXELFORMAT_ATC_RGBA, PIXELFORMAT_BGRA8, PIXELFORMAT_R8I, PIXELFORMAT_R8UI, PIXELFORMAT_R16I, PIXELFORMAT_R16UI, + PIXELFORMAT_R32I, PIXELFORMAT_R32UI, PIXELFORMAT_RG16I, PIXELFORMAT_RG16UI, PIXELFORMAT_RG32I, PIXELFORMAT_RG32UI, + PIXELFORMAT_RG8I, PIXELFORMAT_RG8UI, PIXELFORMAT_RGBA16I, PIXELFORMAT_RGBA16UI, PIXELFORMAT_RGBA32I, PIXELFORMAT_RGBA32UI, + PIXELFORMAT_RGBA8I, PIXELFORMAT_RGBA8UI } from '../constants.js'; // map of PIXELFORMAT_*** to GPUTextureFormat @@ -42,3 +45,21 @@ gpuTextureFormats[PIXELFORMAT_ASTC_4x4] = 'astc-4x4-unorm'; gpuTextureFormats[PIXELFORMAT_ATC_RGB] = ''; gpuTextureFormats[PIXELFORMAT_ATC_RGBA] = ''; gpuTextureFormats[PIXELFORMAT_BGRA8] = 'bgra8unorm'; +gpuTextureFormats[PIXELFORMAT_R8I] = 'r8sint'; +gpuTextureFormats[PIXELFORMAT_R8UI] = 'r8uint'; +gpuTextureFormats[PIXELFORMAT_R16I] = 'r16sint'; +gpuTextureFormats[PIXELFORMAT_R16UI] = 'r16uint'; +gpuTextureFormats[PIXELFORMAT_R32I] = 'r32sint'; +gpuTextureFormats[PIXELFORMAT_R32UI] = 'r32uint'; +gpuTextureFormats[PIXELFORMAT_RG8I] = 'r8sint'; +gpuTextureFormats[PIXELFORMAT_RG8UI] = 'rg8uint'; +gpuTextureFormats[PIXELFORMAT_RG16I] = 'rg16sint'; +gpuTextureFormats[PIXELFORMAT_RG16UI] = 'rg16uint'; +gpuTextureFormats[PIXELFORMAT_RG32I] = 'rg32sint'; +gpuTextureFormats[PIXELFORMAT_RG32UI] = 'rg32uint'; +gpuTextureFormats[PIXELFORMAT_RGBA8I] = 'r8sint'; +gpuTextureFormats[PIXELFORMAT_RGBA8UI] = 'rgba8uint'; +gpuTextureFormats[PIXELFORMAT_RGBA16I] = 'rgba16sint'; +gpuTextureFormats[PIXELFORMAT_RGBA16UI] = 'rgba16uint'; +gpuTextureFormats[PIXELFORMAT_RGBA32I] = 'rgba32sint'; +gpuTextureFormats[PIXELFORMAT_RGBA32UI] = 'rgba32uint'; \ No newline at end of file diff --git a/src/platform/graphics/webgpu/webgpu-texture.js b/src/platform/graphics/webgpu/webgpu-texture.js index 275e2a2a897..618c6e76f20 100644 --- a/src/platform/graphics/webgpu/webgpu-texture.js +++ b/src/platform/graphics/webgpu/webgpu-texture.js @@ -8,7 +8,7 @@ import { PIXELFORMAT_RGBA16F, PIXELFORMAT_RGBA32F, PIXELFORMAT_DEPTHSTENCIL, SAMPLETYPE_UNFILTERABLE_FLOAT, SAMPLETYPE_DEPTH, FILTER_NEAREST, FILTER_LINEAR, FILTER_NEAREST_MIPMAP_NEAREST, FILTER_NEAREST_MIPMAP_LINEAR, - FILTER_LINEAR_MIPMAP_NEAREST, FILTER_LINEAR_MIPMAP_LINEAR + FILTER_LINEAR_MIPMAP_NEAREST, FILTER_LINEAR_MIPMAP_LINEAR, isIntegerPixelFormat } from '../constants.js'; import { TextureUtils } from '../texture-utils.js'; import { WebgpuDebug } from './webgpu-debug.js'; @@ -236,7 +236,7 @@ class WebgpuTexture { // TODO: this is temporary and needs to be made generic if (this.texture.format === PIXELFORMAT_RGBA32F || this.texture.format === PIXELFORMAT_DEPTHSTENCIL || - this.texture.format === PIXELFORMAT_RGBA16F) { + this.texture.format === PIXELFORMAT_RGBA16F || isIntegerPixelFormat(this.texture.format)) { descr.magFilter = 'nearest'; descr.minFilter = 'nearest'; descr.mipmapFilter = 'nearest'; From 441ddefa7e27860a0474117850b8ad054957f5ee Mon Sep 17 00:00:00 2001 From: Liam Don Date: Thu, 28 Dec 2023 11:26:10 -0800 Subject: [PATCH 02/43] Add newline to fix lint --- src/platform/graphics/webgpu/constants.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/graphics/webgpu/constants.js b/src/platform/graphics/webgpu/constants.js index 8a2c3f9db98..cbdd01b78fa 100644 --- a/src/platform/graphics/webgpu/constants.js +++ b/src/platform/graphics/webgpu/constants.js @@ -62,4 +62,4 @@ gpuTextureFormats[PIXELFORMAT_RGBA8UI] = 'rgba8uint'; gpuTextureFormats[PIXELFORMAT_RGBA16I] = 'rgba16sint'; gpuTextureFormats[PIXELFORMAT_RGBA16UI] = 'rgba16uint'; gpuTextureFormats[PIXELFORMAT_RGBA32I] = 'rgba32sint'; -gpuTextureFormats[PIXELFORMAT_RGBA32UI] = 'rgba32uint'; \ No newline at end of file +gpuTextureFormats[PIXELFORMAT_RGBA32UI] = 'rgba32uint'; From fb82f383dcdbc51fee60306ce27a59fc9b8893c7 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Fri, 29 Dec 2023 12:38:11 -0800 Subject: [PATCH 03/43] Fix RG16I string label --- src/platform/graphics/constants.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/graphics/constants.js b/src/platform/graphics/constants.js index 7222594f446..2bbdd3d39a5 100644 --- a/src/platform/graphics/constants.js +++ b/src/platform/graphics/constants.js @@ -793,7 +793,7 @@ export const pixelFormatInfo = new Map([ [PIXELFORMAT_R32UI, { name: 'R32UI', size: 4, channelSize: 4 }], [PIXELFORMAT_RG8I, { name: 'RG8I', size: 2, channelSize: 1 }], [PIXELFORMAT_RG8UI, { name: 'RG8UI', size: 2, channelSize: 1 }], - [PIXELFORMAT_RG16I, { name: 'RG8I', size: 4, channelSize: 2 }], + [PIXELFORMAT_RG16I, { name: 'RG16I', size: 4, channelSize: 2 }], [PIXELFORMAT_RG16UI, { name: 'RG16UI', size: 4, channelSize: 2 }], [PIXELFORMAT_RG32I, { name: 'RG32I', size: 8, channelSize: 4 }], [PIXELFORMAT_RG32UI, { name: 'RG32UI', size: 8, channelSize: 4 }], From a5aff3e6ca16d196c40bc9dd96a380c0e7d2fd6d Mon Sep 17 00:00:00 2001 From: Liam Don Date: Fri, 29 Dec 2023 12:38:40 -0800 Subject: [PATCH 04/43] Add optional check --- src/platform/graphics/constants.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/graphics/constants.js b/src/platform/graphics/constants.js index 2bbdd3d39a5..fb0c374de61 100644 --- a/src/platform/graphics/constants.js +++ b/src/platform/graphics/constants.js @@ -807,7 +807,7 @@ export const pixelFormatInfo = new Map([ // update this function when exposing additional compressed pixel formats export const isCompressedPixelFormat = (format) => { - return pixelFormatInfo.get(format).blockSize !== undefined; + return pixelFormatInfo.get(format)?.blockSize !== undefined; }; export const isIntegerPixelFormat = (format) => { From cf7e085f05230b8b644142d3d10f0753589c96d7 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Thu, 11 Jan 2024 10:38:55 -0800 Subject: [PATCH 05/43] Change comment to Not supported on WebGL1 Co-authored-by: Martin Valigursky <59932779+mvaligursky@users.noreply.github.com> --- src/platform/graphics/constants.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/graphics/constants.js b/src/platform/graphics/constants.js index fb0c374de61..e9d7eaeb15e 100644 --- a/src/platform/graphics/constants.js +++ b/src/platform/graphics/constants.js @@ -784,7 +784,7 @@ export const pixelFormatInfo = new Map([ [PIXELFORMAT_ATC_RGB, { name: 'ATC_RGB', blockSize: 8 }], [PIXELFORMAT_ATC_RGBA, { name: 'ATC_RGBA', blockSize: 16 }], - // uncompressed integer formats (WebGL2 Only) + // uncompressed integer formats (Not supported on WebGL1) [PIXELFORMAT_R8I, { name: 'R8I', size: 1, channelSize: 1 }], [PIXELFORMAT_R8UI, { name: 'R8UI', size: 1, channelSize: 1 }], [PIXELFORMAT_R16I, { name: 'R16I', size: 2, channelSize: 2 }], From e0e9d487f7421d918425b7865f5af3a0ce25ed1d Mon Sep 17 00:00:00 2001 From: Liam Don Date: Thu, 11 Jan 2024 15:34:14 -0800 Subject: [PATCH 06/43] Clean up uniform buffer storage selection and padding --- src/platform/graphics/constants.js | 25 +++++- src/platform/graphics/uniform-buffer.js | 108 ++++++------------------ 2 files changed, 49 insertions(+), 84 deletions(-) diff --git a/src/platform/graphics/constants.js b/src/platform/graphics/constants.js index e9d7eaeb15e..19e56ab4951 100644 --- a/src/platform/graphics/constants.js +++ b/src/platform/graphics/constants.js @@ -1412,7 +1412,6 @@ export const UNIFORMTYPE_TEXTURE2D_ARRAY = 47; export const UNIFORMTYPE_ITEXTURE2D_ARRAY = 48; export const UNIFORMTYPE_UTEXTURE2D_ARRAY = 49; - export const uniformTypeToName = [ // Uniforms 'float', @@ -1485,6 +1484,30 @@ export const uniformTypeToName = [ 'usampler2DArray' ]; +// Map used in uniform-buffer.js to convert uniform type to storage type +// Defaults to TYPE_FLOAT32 for unknown types +export const uniformTypeToStorage = [ + TYPE_FLOAT32, + TYPE_INT32, + TYPE_UINT32, + TYPE_INT32, + + TYPE_FLOAT32, + TYPE_INT32, + TYPE_UINT32, + TYPE_INT32, + + TYPE_FLOAT32, + TYPE_INT32, + TYPE_UINT32, + TYPE_INT32, + + TYPE_FLOAT32, + TYPE_INT32, + TYPE_UINT32, + TYPE_INT32 +]; + /** * A WebGL 1 device type. * diff --git a/src/platform/graphics/uniform-buffer.js b/src/platform/graphics/uniform-buffer.js index 5623d56c040..e4706507620 100644 --- a/src/platform/graphics/uniform-buffer.js +++ b/src/platform/graphics/uniform-buffer.js @@ -1,26 +1,20 @@ import { Debug } from '../../core/debug.js'; import { uniformTypeToName, - UNIFORMTYPE_INT, UNIFORMTYPE_FLOAT, UNIFORMTYPE_BOOL, UNIFORMTYPE_VEC2, - UNIFORMTYPE_VEC3, UNIFORMTYPE_UINT, UNIFORMTYPE_UVEC3, UNIFORMTYPE_UVEC4, - UNIFORMTYPE_VEC4, UNIFORMTYPE_IVEC2, UNIFORMTYPE_IVEC3, UNIFORMTYPE_IVEC4, - UNIFORMTYPE_FLOATARRAY, UNIFORMTYPE_VEC2ARRAY, UNIFORMTYPE_VEC3ARRAY, - UNIFORMTYPE_MAT2, UNIFORMTYPE_MAT3, UNIFORMTYPE_UVEC2, UNIFORMTYPE_IVEC2ARRAY, UNIFORMTYPE_IVEC3ARRAY, UNIFORMTYPE_IVEC4ARRAY, UNIFORMTYPE_UVEC2ARRAY, UNIFORMTYPE_UVEC3ARRAY, UNIFORMTYPE_UVEC4ARRAY, UNIFORMTYPE_VEC4ARRAY, UNIFORMTYPE_BVEC2, UNIFORMTYPE_BVEC4, UNIFORMTYPE_BVEC3, UNIFORMTYPE_BVEC2ARRAY, UNIFORMTYPE_BVEC3ARRAY, UNIFORMTYPE_BVEC4ARRAY + UNIFORMTYPE_MAT2, UNIFORMTYPE_MAT3, uniformTypeToStorage, TYPE_INT32, TYPE_FLOAT32 } from './constants.js'; import { DynamicBufferAllocation } from './dynamic-buffers.js'; -function updateSingleElement(uniformBuffer, value, offset, numComponents) { - const dst = uniformBuffer.getStorageForType(value.type); +function set(dst, value, offset, numComponents, stride = numComponents) { for (let i = 0; i < numComponents; i++) { - dst[offset + i] = value[i]; + dst[offset + i * stride] = value[i]; } } -function updateArray(uniformBuffer, value, offset, numComponents, count) { - const dst = uniformBuffer.getStorageForType(value.type); +function setArray(dst, value, offset, numComponents, count, componentStride = numComponents, arrayStride = componentStride) { for (let i = 0; i < count; i++) { for (let j = 0; j < numComponents; j++) { - dst[offset + i * numComponents + j] = value[i * numComponents + j]; + dst[offset + i * arrayStride + j] = value[i * componentStride + j]; } } } @@ -30,34 +24,14 @@ function updateArray(uniformBuffer, value, offset, numComponents, count) { const _updateFunctions = []; // convert from continuous array to vec2[3] with padding to vec4[2] _updateFunctions[UNIFORMTYPE_MAT2] = (uniformBuffer, value, offset) => { - const dst = uniformBuffer.storageFloat32; - dst[offset] = value[0]; - dst[offset + 1] = value[1]; - - dst[offset + 4] = value[2]; - dst[offset + 5] = value[3]; - - dst[offset + 8] = value[4]; - dst[offset + 9] = value[5]; + setArray(uniformBuffer.storageFloat32, value, offset, 2, 2, 2, 4); }; // convert from continuous array to vec3[3] with padding to vec4[3] _updateFunctions[UNIFORMTYPE_MAT3] = (uniformBuffer, value, offset) => { - const dst = uniformBuffer.storageFloat32; - dst[offset] = value[0]; - dst[offset + 1] = value[1]; - dst[offset + 2] = value[2]; - - dst[offset + 4] = value[3]; - dst[offset + 5] = value[4]; - dst[offset + 6] = value[5]; - - dst[offset + 8] = value[6]; - dst[offset + 9] = value[7]; - dst[offset + 10] = value[8]; + setArray(uniformBuffer.storageFloat32, value, offset, 3, 3, 3, 4); }; - /** * A uniform buffer represents a GPU memory buffer storing the uniforms. * @@ -72,13 +46,13 @@ class UniformBuffer { /** @type {DynamicBufferAllocation} */ allocation; - /** @type {Float32Array} */ + /** @type {Float32Array | undefined} */ storageFloat32; - /** @type {Int32Array} */ + /** @type {Int32Array | undefined} */ storageInt32; - /** @type {Uint32Array} */ + /** @type {Uint32Array | undefined} */ storageUint32; /** @@ -174,16 +148,23 @@ class UniformBuffer { const value = uniformFormat.scopeId.value; if (value !== null && value !== undefined) { + const storageType = uniformTypeToStorage[uniformFormat.type]; + const dst = (!storageType || storageType === TYPE_FLOAT32) ? + this.storageFloat32 : + storageType === TYPE_INT32 ? this.storageInt32 : this.storageUint32; + + if (!dst) { + Debug.error(`Uniform buffer storage is not allocated for type ${uniformTypeToName[uniformFormat.type]}`); + return; + } const updateFunction = _updateFunctions[uniformFormat.updateType]; if (updateFunction) { updateFunction(this, value, offset, uniformFormat.numComponents, uniformFormat.count); + } else if (uniformFormat.isArrayType) { + setArray(dst, value, offset, uniformFormat.numComponents, uniformFormat.count); } else { - if (uniformFormat.isArrayType) { - updateArray(this, value, offset, uniformFormat.numComponents, uniformFormat.count); - } else { - updateSingleElement(this, value, offset, uniformFormat.numComponents); - } + set(dst, value, offset, uniformFormat.numComponents); } } else { Debug.warnOnce(`Value was not set when assigning to uniform [${uniformFormat.name}]` + @@ -231,48 +212,9 @@ class UniformBuffer { // Upload the new data this.impl.unlock(this); } else { - this.storageFloat32 = null; - this.storageInt32 = null; - this.storageUint32 = null; - } - } - - getStorageForType(type) { - switch (type) { - case UNIFORMTYPE_INT: - case UNIFORMTYPE_BOOL: - case UNIFORMTYPE_IVEC2: - case UNIFORMTYPE_IVEC3: - case UNIFORMTYPE_IVEC4: - case UNIFORMTYPE_BVEC2: - case UNIFORMTYPE_BVEC3: - case UNIFORMTYPE_BVEC4: - case UNIFORMTYPE_IVEC2ARRAY: - case UNIFORMTYPE_IVEC3ARRAY: - case UNIFORMTYPE_IVEC4ARRAY: - case UNIFORMTYPE_BVEC2ARRAY: - case UNIFORMTYPE_BVEC3ARRAY: - case UNIFORMTYPE_BVEC4ARRAY: - return this.storageInt32; - case UNIFORMTYPE_UINT: - case UNIFORMTYPE_UVEC2: - case UNIFORMTYPE_UVEC3: - case UNIFORMTYPE_UVEC4: - case UNIFORMTYPE_UVEC2ARRAY: - case UNIFORMTYPE_UVEC3ARRAY: - case UNIFORMTYPE_UVEC4ARRAY: - return this.storageUint32; - case UNIFORMTYPE_FLOAT: - case UNIFORMTYPE_VEC2: - case UNIFORMTYPE_VEC3: - case UNIFORMTYPE_VEC4: - case UNIFORMTYPE_MAT2: - case UNIFORMTYPE_MAT3: - case UNIFORMTYPE_FLOATARRAY: - case UNIFORMTYPE_VEC2ARRAY: - case UNIFORMTYPE_VEC3ARRAY: - case UNIFORMTYPE_VEC4ARRAY: - return this.storageFloat32; + this.storageFloat32 = undefined; + this.storageInt32 = undefined; + this.storageUint32 = undefined; } } } From eadc79357bd35847d7cffa3d6075119eb127b610 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Thu, 11 Jan 2024 15:36:22 -0800 Subject: [PATCH 07/43] Clean up spaces --- src/platform/graphics/constants.js | 58 +++++++++++++++--------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/platform/graphics/constants.js b/src/platform/graphics/constants.js index 19e56ab4951..652735b12bf 100644 --- a/src/platform/graphics/constants.js +++ b/src/platform/graphics/constants.js @@ -770,39 +770,39 @@ export const pixelFormatInfo = new Map([ [PIXELFORMAT_BGRA8, { name: 'BGRA8', size: 4 }], // compressed formats - [PIXELFORMAT_DXT1, { name: 'DXT1', blockSize: 8 }], - [PIXELFORMAT_DXT3, { name: 'DXT3', blockSize: 16 }], - [PIXELFORMAT_DXT5, { name: 'DXT5', blockSize: 16 }], - [PIXELFORMAT_ETC1, { name: 'ETC1', blockSize: 8 }], - [PIXELFORMAT_ETC2_RGB, { name: 'ETC2_RGB', blockSize: 8 }], - [PIXELFORMAT_ETC2_RGBA, { name: 'ETC2_RGBA', blockSize: 16 }], - [PIXELFORMAT_PVRTC_2BPP_RGB_1, { name: 'PVRTC_2BPP_RGB_1', blockSize: 8 }], + [PIXELFORMAT_DXT1, { name: 'DXT1', blockSize: 8 }], + [PIXELFORMAT_DXT3, { name: 'DXT3', blockSize: 16 }], + [PIXELFORMAT_DXT5, { name: 'DXT5', blockSize: 16 }], + [PIXELFORMAT_ETC1, { name: 'ETC1', blockSize: 8 }], + [PIXELFORMAT_ETC2_RGB, { name: 'ETC2_RGB', blockSize: 8 }], + [PIXELFORMAT_ETC2_RGBA, { name: 'ETC2_RGBA', blockSize: 16 }], + [PIXELFORMAT_PVRTC_2BPP_RGB_1, { name: 'PVRTC_2BPP_RGB_1', blockSize: 8 }], [PIXELFORMAT_PVRTC_2BPP_RGBA_1, { name: 'PVRTC_2BPP_RGBA_1', blockSize: 8 }], - [PIXELFORMAT_PVRTC_4BPP_RGB_1, { name: 'PVRTC_4BPP_RGB_1', blockSize: 8 }], + [PIXELFORMAT_PVRTC_4BPP_RGB_1, { name: 'PVRTC_4BPP_RGB_1', blockSize: 8 }], [PIXELFORMAT_PVRTC_4BPP_RGBA_1, { name: 'PVRTC_4BPP_RGBA_1', blockSize: 8 }], - [PIXELFORMAT_ASTC_4x4, { name: 'ASTC_4x4', blockSize: 16 }], - [PIXELFORMAT_ATC_RGB, { name: 'ATC_RGB', blockSize: 8 }], - [PIXELFORMAT_ATC_RGBA, { name: 'ATC_RGBA', blockSize: 16 }], + [PIXELFORMAT_ASTC_4x4, { name: 'ASTC_4x4', blockSize: 16 }], + [PIXELFORMAT_ATC_RGB, { name: 'ATC_RGB', blockSize: 8 }], + [PIXELFORMAT_ATC_RGBA, { name: 'ATC_RGBA', blockSize: 16 }], // uncompressed integer formats (Not supported on WebGL1) - [PIXELFORMAT_R8I, { name: 'R8I', size: 1, channelSize: 1 }], - [PIXELFORMAT_R8UI, { name: 'R8UI', size: 1, channelSize: 1 }], - [PIXELFORMAT_R16I, { name: 'R16I', size: 2, channelSize: 2 }], - [PIXELFORMAT_R16UI, { name: 'R16UI', size: 2, channelSize: 2 }], - [PIXELFORMAT_R32I, { name: 'R32I', size: 4, channelSize: 4 }], - [PIXELFORMAT_R32UI, { name: 'R32UI', size: 4, channelSize: 4 }], - [PIXELFORMAT_RG8I, { name: 'RG8I', size: 2, channelSize: 1 }], - [PIXELFORMAT_RG8UI, { name: 'RG8UI', size: 2, channelSize: 1 }], - [PIXELFORMAT_RG16I, { name: 'RG16I', size: 4, channelSize: 2 }], - [PIXELFORMAT_RG16UI, { name: 'RG16UI', size: 4, channelSize: 2 }], - [PIXELFORMAT_RG32I, { name: 'RG32I', size: 8, channelSize: 4 }], - [PIXELFORMAT_RG32UI, { name: 'RG32UI', size: 8, channelSize: 4 }], - [PIXELFORMAT_RGBA8I, { name: 'RGBA8I', size: 4, channelSize: 1 }], - [PIXELFORMAT_RGBA8UI, { name: 'RGBA8UI', size: 4, channelSize: 1 }], - [PIXELFORMAT_RGBA16I, { name: 'RGBA16I', size: 8, channelSize: 2 }], - [PIXELFORMAT_RGBA16UI, { name: 'RGBA16UI', size: 8, channelSize: 2 }], - [PIXELFORMAT_RGBA32I, { name: 'RGBA32I', size: 16, channelSize: 4 }], - [PIXELFORMAT_RGBA32UI, { name: 'RGBA32UI', size: 16, channelSize: 4 }] + [PIXELFORMAT_R8I, { name: 'R8I', size: 1, channelSize: 1 }], + [PIXELFORMAT_R8UI, { name: 'R8UI', size: 1, channelSize: 1 }], + [PIXELFORMAT_R16I, { name: 'R16I', size: 2, channelSize: 2 }], + [PIXELFORMAT_R16UI, { name: 'R16UI', size: 2, channelSize: 2 }], + [PIXELFORMAT_R32I, { name: 'R32I', size: 4, channelSize: 4 }], + [PIXELFORMAT_R32UI, { name: 'R32UI', size: 4, channelSize: 4 }], + [PIXELFORMAT_RG8I, { name: 'RG8I', size: 2, channelSize: 1 }], + [PIXELFORMAT_RG8UI, { name: 'RG8UI', size: 2, channelSize: 1 }], + [PIXELFORMAT_RG16I, { name: 'RG16I', size: 4, channelSize: 2 }], + [PIXELFORMAT_RG16UI, { name: 'RG16UI', size: 4, channelSize: 2 }], + [PIXELFORMAT_RG32I, { name: 'RG32I', size: 8, channelSize: 4 }], + [PIXELFORMAT_RG32UI, { name: 'RG32UI', size: 8, channelSize: 4 }], + [PIXELFORMAT_RGBA8I, { name: 'RGBA8I', size: 4, channelSize: 1 }], + [PIXELFORMAT_RGBA8UI, { name: 'RGBA8UI', size: 4, channelSize: 1 }], + [PIXELFORMAT_RGBA16I, { name: 'RGBA16I', size: 8, channelSize: 2 }], + [PIXELFORMAT_RGBA16UI, { name: 'RGBA16UI', size: 8, channelSize: 2 }], + [PIXELFORMAT_RGBA32I, { name: 'RGBA32I', size: 16, channelSize: 4 }], + [PIXELFORMAT_RGBA32UI, { name: 'RGBA32UI', size: 16, channelSize: 4 }] ]); // update this function when exposing additional compressed pixel formats From faed1b2160dc39b8b80938e28489dee03e7138bc Mon Sep 17 00:00:00 2001 From: Liam Don Date: Thu, 11 Jan 2024 15:38:57 -0800 Subject: [PATCH 08/43] Update WebGL1 not supported comment --- src/platform/graphics/constants.js | 38 +++++++++++++++--------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/platform/graphics/constants.js b/src/platform/graphics/constants.js index 652735b12bf..042e172c07c 100644 --- a/src/platform/graphics/constants.js +++ b/src/platform/graphics/constants.js @@ -169,7 +169,7 @@ export const BUFFER_STREAM = 2; /** * The data store contents will be modified repeatedly on the GPU and used many times. Optimal for - * transform feedback usage (WebGL2 only). + * transform feedback usage (not supported by WebGL1). * * @type {number} */ @@ -619,126 +619,126 @@ export const PIXELFORMAT_ATC_RGBA = 30; export const PIXELFORMAT_BGRA8 = 31; /** - * 8-bit signed integer single-channel (R) format (WebGL2 only). + * 8-bit signed integer single-channel (R) format (Not supported by WebGL1). * * @type {number} */ export const PIXELFORMAT_R8I = 32; /** - * 8-bit unsigned integer single-channel (R) format (WebGL2 only). + * 8-bit unsigned integer single-channel (R) format (Not supported by WebGL1). * * @type {number} */ export const PIXELFORMAT_R8UI = 33; /** - * 16-bit signed integer single-channel (R) format (WebGL2 only). + * 16-bit signed integer single-channel (R) format (Not supported by WebGL1). * * @type {number} */ export const PIXELFORMAT_R16I = 34; /** - * 16-bit unsigned integer single-channel (R) format (WebGL2 only). + * 16-bit unsigned integer single-channel (R) format (Not supported by WebGL1). * * @type {number} */ export const PIXELFORMAT_R16UI = 35; /** - * 32-bit signed integer single-channel (R) format (WebGL2 only). + * 32-bit signed integer single-channel (R) format (Not supported by WebGL1). * * @type {number} */ export const PIXELFORMAT_R32I = 36; /** - * 32-bit unsigned integer single-channel (R) format (WebGL2 only). + * 32-bit unsigned integer single-channel (R) format (Not supported by WebGL1). * * @type {number} */ export const PIXELFORMAT_R32UI = 37; /** - * 8-bit per-channel signed integer (RG) format (WebGL2 only). + * 8-bit per-channel signed integer (RG) format (Not supported by WebGL1). * * @type {number} */ export const PIXELFORMAT_RG8I = 38; /** - * 8-bit per-channel unsigned integer (RG) format (WebGL2 only). + * 8-bit per-channel unsigned integer (RG) format (Not supported by WebGL1). * * @type {number} */ export const PIXELFORMAT_RG8UI = 39; /** - * 16-bit per-channel signed integer (RG) format (WebGL2 only). + * 16-bit per-channel signed integer (RG) format (Not supported by WebGL1). * * @type {number} */ export const PIXELFORMAT_RG16I = 40; /** - * 16-bit per-channel unsigned integer (RG) format (WebGL2 only). + * 16-bit per-channel unsigned integer (RG) format (Not supported by WebGL1). * * @type {number} */ export const PIXELFORMAT_RG16UI = 41; /** - * 32-bit per-channel signed integer (RG) format (WebGL2 only). + * 32-bit per-channel signed integer (RG) format (Not supported by WebGL1). * * @type {number} */ export const PIXELFORMAT_RG32I = 42; /** - * 32-bit per-channel unsigned integer (RG) format (WebGL2 only). + * 32-bit per-channel unsigned integer (RG) format (Not supported by WebGL1). * * @type {number} */ export const PIXELFORMAT_RG32UI = 43; /** - * 8-bit per-channel signed integer (RGBA) format (WebGL2 only). + * 8-bit per-channel signed integer (RGBA) format (Not supported by WebGL1). * * @type {number} */ export const PIXELFORMAT_RGBA8I = 44; /** - * 8-bit per-channel unsigned integer (RGBA) format (WebGL2 only). + * 8-bit per-channel unsigned integer (RGBA) format (Not supported by WebGL1). * * @type {number} */ export const PIXELFORMAT_RGBA8UI = 45; /** - * 16-bit per-channel signed integer (RGBA) format (WebGL2 only). + * 16-bit per-channel signed integer (RGBA) format (Not supported by WebGL1). * * @type {number} */ export const PIXELFORMAT_RGBA16I = 46; /** - * 16-bit per-channel unsigned integer (RGBA) format (WebGL2 only). + * 16-bit per-channel unsigned integer (RGBA) format (Not supported by WebGL1). * * @type {number} */ export const PIXELFORMAT_RGBA16UI = 47; /** - * 32-bit per-channel signed integer (RGBA) format (WebGL2 only). + * 32-bit per-channel signed integer (RGBA) format (Not supported by WebGL1). * * @type {number} */ export const PIXELFORMAT_RGBA32I = 48; /** - * 32-bit per-channel unsigned integer (RGBA) format (WebGL2 only). + * 32-bit per-channel unsigned integer (RGBA) format (Not supported by WebGL1). * * @type {number} */ From 0aad9e79bc16d6b9cc279cbe8e56749ac4f28130 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Thu, 11 Jan 2024 15:55:06 -0800 Subject: [PATCH 09/43] Use boolean flag to indicate integer pixel format --- src/platform/graphics/constants.js | 38 +++++++++++++++--------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/platform/graphics/constants.js b/src/platform/graphics/constants.js index 042e172c07c..61316a12489 100644 --- a/src/platform/graphics/constants.js +++ b/src/platform/graphics/constants.js @@ -785,24 +785,24 @@ export const pixelFormatInfo = new Map([ [PIXELFORMAT_ATC_RGBA, { name: 'ATC_RGBA', blockSize: 16 }], // uncompressed integer formats (Not supported on WebGL1) - [PIXELFORMAT_R8I, { name: 'R8I', size: 1, channelSize: 1 }], - [PIXELFORMAT_R8UI, { name: 'R8UI', size: 1, channelSize: 1 }], - [PIXELFORMAT_R16I, { name: 'R16I', size: 2, channelSize: 2 }], - [PIXELFORMAT_R16UI, { name: 'R16UI', size: 2, channelSize: 2 }], - [PIXELFORMAT_R32I, { name: 'R32I', size: 4, channelSize: 4 }], - [PIXELFORMAT_R32UI, { name: 'R32UI', size: 4, channelSize: 4 }], - [PIXELFORMAT_RG8I, { name: 'RG8I', size: 2, channelSize: 1 }], - [PIXELFORMAT_RG8UI, { name: 'RG8UI', size: 2, channelSize: 1 }], - [PIXELFORMAT_RG16I, { name: 'RG16I', size: 4, channelSize: 2 }], - [PIXELFORMAT_RG16UI, { name: 'RG16UI', size: 4, channelSize: 2 }], - [PIXELFORMAT_RG32I, { name: 'RG32I', size: 8, channelSize: 4 }], - [PIXELFORMAT_RG32UI, { name: 'RG32UI', size: 8, channelSize: 4 }], - [PIXELFORMAT_RGBA8I, { name: 'RGBA8I', size: 4, channelSize: 1 }], - [PIXELFORMAT_RGBA8UI, { name: 'RGBA8UI', size: 4, channelSize: 1 }], - [PIXELFORMAT_RGBA16I, { name: 'RGBA16I', size: 8, channelSize: 2 }], - [PIXELFORMAT_RGBA16UI, { name: 'RGBA16UI', size: 8, channelSize: 2 }], - [PIXELFORMAT_RGBA32I, { name: 'RGBA32I', size: 16, channelSize: 4 }], - [PIXELFORMAT_RGBA32UI, { name: 'RGBA32UI', size: 16, channelSize: 4 }] + [PIXELFORMAT_R8I, { name: 'R8I', size: 1, isInt: true }], + [PIXELFORMAT_R8UI, { name: 'R8UI', size: 1, isInt: true }], + [PIXELFORMAT_R16I, { name: 'R16I', size: 2, isInt: true }], + [PIXELFORMAT_R16UI, { name: 'R16UI', size: 2, isInt: true }], + [PIXELFORMAT_R32I, { name: 'R32I', size: 4, isInt: true }], + [PIXELFORMAT_R32UI, { name: 'R32UI', size: 4, isInt: true }], + [PIXELFORMAT_RG8I, { name: 'RG8I', size: 2, isInt: true }], + [PIXELFORMAT_RG8UI, { name: 'RG8UI', size: 2, isInt: true }], + [PIXELFORMAT_RG16I, { name: 'RG16I', size: 4, isInt: true }], + [PIXELFORMAT_RG16UI, { name: 'RG16UI', size: 4, isInt: true }], + [PIXELFORMAT_RG32I, { name: 'RG32I', size: 8, isInt: true }], + [PIXELFORMAT_RG32UI, { name: 'RG32UI', size: 8, isInt: true }], + [PIXELFORMAT_RGBA8I, { name: 'RGBA8I', size: 4, isInt: true }], + [PIXELFORMAT_RGBA8UI, { name: 'RGBA8UI', size: 4, isInt: true }], + [PIXELFORMAT_RGBA16I, { name: 'RGBA16I', size: 8, isInt: true }], + [PIXELFORMAT_RGBA16UI, { name: 'RGBA16UI', size: 8, isInt: true }], + [PIXELFORMAT_RGBA32I, { name: 'RGBA32I', size: 16, isInt: true }], + [PIXELFORMAT_RGBA32UI, { name: 'RGBA32UI', size: 16, isInt: true }] ]); // update this function when exposing additional compressed pixel formats @@ -811,7 +811,7 @@ export const isCompressedPixelFormat = (format) => { }; export const isIntegerPixelFormat = (format) => { - return pixelFormatInfo.get(format)?.channelSize !== undefined; + return pixelFormatInfo.get(format)?.isInt === true; }; // get the pixel format array type From d120a5eb1d59463d5e648b9ff2d5a85f1890c8dc Mon Sep 17 00:00:00 2001 From: Liam Don Date: Thu, 11 Jan 2024 17:18:01 -0800 Subject: [PATCH 10/43] Reorder constants and fix webgpu typo --- src/platform/graphics/constants.js | 256 +++++++++--------- .../graphics/uniform-buffer-format.js | 36 ++- src/platform/graphics/uniform-buffer.js | 7 +- src/platform/graphics/webgpu/constants.js | 4 +- 4 files changed, 149 insertions(+), 154 deletions(-) diff --git a/src/platform/graphics/constants.js b/src/platform/graphics/constants.js index 61316a12489..0201960c8b9 100644 --- a/src/platform/graphics/constants.js +++ b/src/platform/graphics/constants.js @@ -1339,174 +1339,172 @@ export const TYPE_FLOAT32 = 6; */ export const TYPE_FLOAT16 = 7; -/** - * Uniform and sampler types - */ -// Uniforms -export const UNIFORMTYPE_FLOAT = 0; +// Uniform types +export const UNIFORMTYPE_BOOL = 0; export const UNIFORMTYPE_INT = 1; -export const UNIFORMTYPE_UINT = 2; -export const UNIFORMTYPE_BOOL = 3; - -export const UNIFORMTYPE_VEC2 = 4; -export const UNIFORMTYPE_IVEC2 = 5; -export const UNIFORMTYPE_UVEC2 = 6; -export const UNIFORMTYPE_BVEC2 = 7; - -export const UNIFORMTYPE_VEC3 = 8; -export const UNIFORMTYPE_IVEC3 = 9; -export const UNIFORMTYPE_UVEC3 = 10; -export const UNIFORMTYPE_BVEC3 = 11; - -export const UNIFORMTYPE_VEC4 = 12; -export const UNIFORMTYPE_IVEC4 = 13; -export const UNIFORMTYPE_UVEC4 = 14; -export const UNIFORMTYPE_BVEC4 = 15; - -export const UNIFORMTYPE_MAT2 = 16; -export const UNIFORMTYPE_MAT3 = 17; -export const UNIFORMTYPE_MAT4 = 18; - -// Uniform buffers -export const UNIFORMTYPE_FLOATARRAY = 19; -export const UNIFORMTYPE_INTARRAY = 20; -export const UNIFORMTYPE_UINTARRAY = 21; -export const UNIFORMTYPE_BOOLARRAY = 22; - -export const UNIFORMTYPE_VEC2ARRAY = 23; -export const UNIFORMTYPE_IVEC2ARRAY = 24; -export const UNIFORMTYPE_UVEC2ARRAY = 25; -export const UNIFORMTYPE_BVEC2ARRAY = 26; - -export const UNIFORMTYPE_VEC3ARRAY = 27; -export const UNIFORMTYPE_IVEC3ARRAY = 28; -export const UNIFORMTYPE_UVEC3ARRAY = 29; -export const UNIFORMTYPE_BVEC3ARRAY = 30; - -export const UNIFORMTYPE_VEC4ARRAY = 31; -export const UNIFORMTYPE_IVEC4ARRAY = 32; -export const UNIFORMTYPE_UVEC4ARRAY = 33; -export const UNIFORMTYPE_BVEC4ARRAY = 34; - -export const UNIFORMTYPE_MAT4ARRAY = 35; - -// Samplers: 2D -export const UNIFORMTYPE_TEXTURE2D = 36; -export const UNIFORMTYPE_ITEXTURE2D = 37; -export const UNIFORMTYPE_UTEXTURE2D = 38; -export const UNIFORMTYPE_TEXTURE2D_SHADOW = 39; - -// Samplers: Cube -export const UNIFORMTYPE_TEXTURECUBE = 40; -export const UNIFORMTYPE_ITEXTURECUBE = 41; -export const UNIFORMTYPE_UTEXTURECUBE = 42; -export const UNIFORMTYPE_TEXTURECUBE_SHADOW = 43; - -// Samplers: 3D -export const UNIFORMTYPE_TEXTURE3D = 44; -export const UNIFORMTYPE_ITEXTURE3D = 45; -export const UNIFORMTYPE_UTEXTURE3D = 46; - -// Samplers Texture Array -export const UNIFORMTYPE_TEXTURE2D_ARRAY = 47; +export const UNIFORMTYPE_FLOAT = 2; +export const UNIFORMTYPE_VEC2 = 3; +export const UNIFORMTYPE_VEC3 = 4; +export const UNIFORMTYPE_VEC4 = 5; +export const UNIFORMTYPE_IVEC2 = 6; +export const UNIFORMTYPE_IVEC3 = 7; +export const UNIFORMTYPE_IVEC4 = 8; +export const UNIFORMTYPE_BVEC2 = 9; +export const UNIFORMTYPE_BVEC3 = 10; +export const UNIFORMTYPE_BVEC4 = 11; +export const UNIFORMTYPE_MAT2 = 12; +export const UNIFORMTYPE_MAT3 = 13; +export const UNIFORMTYPE_MAT4 = 14; +export const UNIFORMTYPE_TEXTURE2D = 15; +export const UNIFORMTYPE_TEXTURECUBE = 16; +export const UNIFORMTYPE_FLOATARRAY = 17; +export const UNIFORMTYPE_TEXTURE2D_SHADOW = 18; +export const UNIFORMTYPE_TEXTURECUBE_SHADOW = 19; +export const UNIFORMTYPE_TEXTURE3D = 20; +export const UNIFORMTYPE_VEC2ARRAY = 21; +export const UNIFORMTYPE_VEC3ARRAY = 22; +export const UNIFORMTYPE_VEC4ARRAY = 23; +export const UNIFORMTYPE_MAT4ARRAY = 24; +export const UNIFORMTYPE_TEXTURE2D_ARRAY = 25; + +// Unsigned uniform types +export const UNIFORMTYPE_UINT = 26; +export const UNIFORMTYPE_UVEC2 = 27; +export const UNIFORMTYPE_UVEC3 = 28; +export const UNIFORMTYPE_UVEC4 = 29; + +// Integer uniform array types +export const UNIFORMTYPE_INTARRAY = 30; +export const UNIFORMTYPE_UINTARRAY = 31; +export const UNIFORMTYPE_BOOLARRAY = 32; +export const UNIFORMTYPE_IVEC2ARRAY = 33; +export const UNIFORMTYPE_UVEC2ARRAY = 34; +export const UNIFORMTYPE_BVEC2ARRAY = 35; +export const UNIFORMTYPE_IVEC3ARRAY = 36; +export const UNIFORMTYPE_UVEC3ARRAY = 37; +export const UNIFORMTYPE_BVEC3ARRAY = 38; +export const UNIFORMTYPE_IVEC4ARRAY = 39; +export const UNIFORMTYPE_UVEC4ARRAY = 40; +export const UNIFORMTYPE_BVEC4ARRAY = 41; + +// Integer texture types +export const UNIFORMTYPE_ITEXTURE2D = 42; +export const UNIFORMTYPE_UTEXTURE2D = 43; +export const UNIFORMTYPE_ITEXTURECUBE = 44; +export const UNIFORMTYPE_UTEXTURECUBE = 45; +export const UNIFORMTYPE_ITEXTURE3D = 46; +export const UNIFORMTYPE_UTEXTURE3D = 47; export const UNIFORMTYPE_ITEXTURE2D_ARRAY = 48; export const UNIFORMTYPE_UTEXTURE2D_ARRAY = 49; export const uniformTypeToName = [ // Uniforms - 'float', - 'int', - 'uint', 'bool', - + 'int', + 'float', 'vec2', - 'ivec2', - 'uvec2', - 'bvec2', - 'vec3', - 'ivec3', - 'uvec3', - 'bvec3', - 'vec4', + 'ivec2', + 'ivec3', 'ivec4', - 'uvec4', + 'bvec2', + 'bvec3', 'bvec4', - 'mat2', 'mat3', 'mat4', - - // Uniform buffers + 'sampler2D', + 'samplerCube', '', // not directly handled: UNIFORMTYPE_FLOATARRAY + 'sampler2DShadow', + 'samplerCubeShadow', + 'sampler3D', + '', // not directly handled: UNIFORMTYPE_VEC2ARRAY + '', // not directly handled: UNIFORMTYPE_VEC3ARRAY + '', // not directly handled: UNIFORMTYPE_VEC4ARRAY + '', // not directly handled: UNIFORMTYPE_MAT4ARRAY + 'sampler2DArray', + 'uint', + 'uvec2', + 'uvec3', + 'uvec4', '', // not directly handled: UNIFORMTYPE_INTARRAY '', // not directly handled: UNIFORMTYPE_UINTARRAY '', // not directly handled: UNIFORMTYPE_BOOLARRAY - - '', // not directly handled: UNIFORMTYPE_VEC2ARRAY '', // not directly handled: UNIFORMTYPE_IVEC2ARRAY '', // not directly handled: UNIFORMTYPE_UVEC2ARRAY '', // not directly handled: UNIFORMTYPE_BVEC2ARRAY - - '', // not directly handled: UNIFORMTYPE_VEC3ARRAY '', // not directly handled: UNIFORMTYPE_IVEC3ARRAY '', // not directly handled: UNIFORMTYPE_UVEC3ARRAY '', // not directly handled: UNIFORMTYPE_BVEC3ARRAY - - '', // not directly handled: UNIFORMTYPE_VEC4ARRAY '', // not directly handled: UNIFORMTYPE_IVEC4ARRAY '', // not directly handled: UNIFORMTYPE_UVEC4ARRAY '', // not directly handled: UNIFORMTYPE_BVEC4ARRAY - - '', // not directly handled: UNIFORMTYPE_MAT4ARRAY - - // Samplers: 2D - 'sampler2D', 'isampler2D', 'usampler2D', - 'sampler2DShadow', - - // Samplers: Cube - 'samplerCube', 'isamplerCube', 'usamplerCube', - 'samplerCubeShadow', - - // Samplers: 3D - 'sampler3D', 'isampler3D', 'usampler3D', - - // Samplers Texture Array - 'sampler2DArray', 'isampler2DArray', 'usampler2DArray' ]; // Map used in uniform-buffer.js to convert uniform type to storage type -// Defaults to TYPE_FLOAT32 for unknown types -export const uniformTypeToStorage = [ - TYPE_FLOAT32, - TYPE_INT32, - TYPE_UINT32, - TYPE_INT32, - - TYPE_FLOAT32, - TYPE_INT32, - TYPE_UINT32, - TYPE_INT32, - - TYPE_FLOAT32, - TYPE_INT32, - TYPE_UINT32, - TYPE_INT32, - - TYPE_FLOAT32, - TYPE_INT32, - TYPE_UINT32, - TYPE_INT32 -]; +// used in the uniform buffer. +export const uniformTypeToStorage = new Uint8Array([ + TYPE_INT32, // UNIFORMTYPE_BOOL + TYPE_INT32, // UNIFORMTYPE_INT + TYPE_FLOAT32, // UNIFORMTYPE_FLOAT + TYPE_FLOAT32, // UNIFORMTYPE_VEC2 + TYPE_FLOAT32, // UNIFORMTYPE_VEC3 + TYPE_FLOAT32, // UNIFORMTYPE_VEC4 + TYPE_INT32, // UNIFORMTYPE_IVEC2 + TYPE_INT32, // UNIFORMTYPE_IVEC3 + TYPE_INT32, // UNIFORMTYPE_IVEC4 + TYPE_INT32, // UNIFORMTYPE_BVEC2 + TYPE_INT32, // UNIFORMTYPE_BVEC3 + TYPE_INT32, // UNIFORMTYPE_BVEC4 + TYPE_FLOAT32, // UNIFORMTYPE_MAT2 + TYPE_FLOAT32, // UNIFORMTYPE_MAT3 + TYPE_FLOAT32, // UNIFORMTYPE_MAT4 + TYPE_INT32, // UNIFORMTYPE_TEXTURE2D + TYPE_INT32, // UNIFORMTYPE_TEXTURECUBE + TYPE_FLOAT32, // UNIFORMTYPE_FLOATARRAY + TYPE_INT32, // UNIFORMTYPE_TEXTURE2D_SHADOW + TYPE_INT32, // UNIFORMTYPE_TEXTURECUBE_SHADOW + TYPE_INT32, // UNIFORMTYPE_TEXTURE3D + TYPE_FLOAT32, // UNIFORMTYPE_VEC2ARRAY + TYPE_FLOAT32, // UNIFORMTYPE_VEC3ARRAY + TYPE_FLOAT32, // UNIFORMTYPE_VEC4ARRAY + TYPE_FLOAT32, // UNIFORMTYPE_MAT4ARRAY + TYPE_INT32, // UNIFORMTYPE_TEXTURE2D_ARRAY + TYPE_UINT32, // UNIFORMTYPE_UINT + TYPE_UINT32, // UNIFORMTYPE_UVEC2 + TYPE_UINT32, // UNIFORMTYPE_UVEC3 + TYPE_UINT32, // UNIFORMTYPE_UVEC4 + TYPE_INT32, // UNIFORMTYPE_INTARRAY + TYPE_UINT32, // UNIFORMTYPE_UINTARRAY + TYPE_INT32, // UNIFORMTYPE_BOOLARRAY + TYPE_INT32, // UNIFORMTYPE_IVEC2ARRAY + TYPE_UINT32, // UNIFORMTYPE_UVEC2ARRAY + TYPE_INT32, // UNIFORMTYPE_BVEC2ARRAY + TYPE_INT32, // UNIFORMTYPE_IVEC3ARRAY + TYPE_UINT32, // UNIFORMTYPE_UVEC3ARRAY + TYPE_INT32, // UNIFORMTYPE_BVEC3ARRAY + TYPE_INT32, // UNIFORMTYPE_IVEC4ARRAY + TYPE_UINT32, // UNIFORMTYPE_UVEC4ARRAY + TYPE_INT32, // UNIFORMTYPE_BVEC4ARRAY + TYPE_INT32, // UNIFORMTYPE_ITEXTURE2D + TYPE_UINT32, // UNIFORMTYPE_UTEXTURE2D + TYPE_INT32, // UNIFORMTYPE_ITEXTURECUBE + TYPE_UINT32, // UNIFORMTYPE_UTEXTURECUBE + TYPE_INT32, // UNIFORMTYPE_ITEXTURE3D + TYPE_UINT32, // UNIFORMTYPE_UTEXTURE3D + TYPE_INT32, // UNIFORMTYPE_ITEXTURE2D_ARRAY + TYPE_UINT32 // UNIFORMTYPE_UTEXTURE2D_ARRAY +]); /** * A WebGL 1 device type. diff --git a/src/platform/graphics/uniform-buffer-format.js b/src/platform/graphics/uniform-buffer-format.js index 456e2e6f9c1..189e5fb4806 100644 --- a/src/platform/graphics/uniform-buffer-format.js +++ b/src/platform/graphics/uniform-buffer-format.js @@ -15,28 +15,25 @@ import { // map of UNIFORMTYPE_*** to number of 32bit components const uniformTypeToNumComponents = []; uniformTypeToNumComponents[UNIFORMTYPE_FLOAT] = 1; -uniformTypeToNumComponents[UNIFORMTYPE_INT] = 1; -uniformTypeToNumComponents[UNIFORMTYPE_UINT] = 1; -uniformTypeToNumComponents[UNIFORMTYPE_BOOL] = 1; - uniformTypeToNumComponents[UNIFORMTYPE_VEC2] = 2; -uniformTypeToNumComponents[UNIFORMTYPE_IVEC2] = 2; -uniformTypeToNumComponents[UNIFORMTYPE_UVEC2] = 2; -uniformTypeToNumComponents[UNIFORMTYPE_BVEC2] = 2; - uniformTypeToNumComponents[UNIFORMTYPE_VEC3] = 3; -uniformTypeToNumComponents[UNIFORMTYPE_IVEC3] = 3; -uniformTypeToNumComponents[UNIFORMTYPE_UVEC3] = 3; -uniformTypeToNumComponents[UNIFORMTYPE_BVEC3] = 3; - uniformTypeToNumComponents[UNIFORMTYPE_VEC4] = 4; +uniformTypeToNumComponents[UNIFORMTYPE_INT] = 1; +uniformTypeToNumComponents[UNIFORMTYPE_IVEC2] = 2; +uniformTypeToNumComponents[UNIFORMTYPE_IVEC3] = 3; uniformTypeToNumComponents[UNIFORMTYPE_IVEC4] = 4; -uniformTypeToNumComponents[UNIFORMTYPE_UVEC4] = 4; +uniformTypeToNumComponents[UNIFORMTYPE_BOOL] = 1; +uniformTypeToNumComponents[UNIFORMTYPE_BVEC2] = 2; +uniformTypeToNumComponents[UNIFORMTYPE_BVEC3] = 3; uniformTypeToNumComponents[UNIFORMTYPE_BVEC4] = 4; - uniformTypeToNumComponents[UNIFORMTYPE_MAT2] = 8; // 2 x vec4 uniformTypeToNumComponents[UNIFORMTYPE_MAT3] = 12; // 3 x vec4 uniformTypeToNumComponents[UNIFORMTYPE_MAT4] = 16; // 4 x vec4 +uniformTypeToNumComponents[UNIFORMTYPE_UINT] = 1; +uniformTypeToNumComponents[UNIFORMTYPE_UVEC2] = 2; +uniformTypeToNumComponents[UNIFORMTYPE_UVEC3] = 3; +uniformTypeToNumComponents[UNIFORMTYPE_UVEC4] = 4; + /** * A class storing description of an individual uniform, stored inside a uniform buffer. @@ -142,13 +139,14 @@ class UniformFormat { this.invalid = true; }); - let elementSize = this.numElements; + let componentSize = this.numComponents; - // element size for arrays is aligned up to vec4 - if (count) - elementSize = math.roundUp(elementSize, 4); + // component size for arrays is aligned up to vec4 + if (count) { + componentSize = math.roundUp(componentSize, 4); + } - this.byteSize = elementSize * 4; + this.byteSize = componentSize * 4; if (count) this.byteSize *= count; diff --git a/src/platform/graphics/uniform-buffer.js b/src/platform/graphics/uniform-buffer.js index e4706507620..a4f7b5de827 100644 --- a/src/platform/graphics/uniform-buffer.js +++ b/src/platform/graphics/uniform-buffer.js @@ -1,7 +1,7 @@ import { Debug } from '../../core/debug.js'; import { uniformTypeToName, - UNIFORMTYPE_MAT2, UNIFORMTYPE_MAT3, uniformTypeToStorage, TYPE_INT32, TYPE_FLOAT32 + UNIFORMTYPE_MAT2, UNIFORMTYPE_MAT3, uniformTypeToStorage, TYPE_INT32, TYPE_FLOAT32, TYPE_UINT32 } from './constants.js'; import { DynamicBufferAllocation } from './dynamic-buffers.js'; @@ -149,9 +149,8 @@ class UniformBuffer { if (value !== null && value !== undefined) { const storageType = uniformTypeToStorage[uniformFormat.type]; - const dst = (!storageType || storageType === TYPE_FLOAT32) ? - this.storageFloat32 : - storageType === TYPE_INT32 ? this.storageInt32 : this.storageUint32; + const dst = storageType === TYPE_INT32 ? this.storageInt32 : + storageType === TYPE_UINT32 ? this.storageUint32 : this.storageFloat32; if (!dst) { Debug.error(`Uniform buffer storage is not allocated for type ${uniformTypeToName[uniformFormat.type]}`); diff --git a/src/platform/graphics/webgpu/constants.js b/src/platform/graphics/webgpu/constants.js index cbdd01b78fa..ec5e4e5da08 100644 --- a/src/platform/graphics/webgpu/constants.js +++ b/src/platform/graphics/webgpu/constants.js @@ -51,13 +51,13 @@ gpuTextureFormats[PIXELFORMAT_R16I] = 'r16sint'; gpuTextureFormats[PIXELFORMAT_R16UI] = 'r16uint'; gpuTextureFormats[PIXELFORMAT_R32I] = 'r32sint'; gpuTextureFormats[PIXELFORMAT_R32UI] = 'r32uint'; -gpuTextureFormats[PIXELFORMAT_RG8I] = 'r8sint'; +gpuTextureFormats[PIXELFORMAT_RG8I] = 'rg8sint'; gpuTextureFormats[PIXELFORMAT_RG8UI] = 'rg8uint'; gpuTextureFormats[PIXELFORMAT_RG16I] = 'rg16sint'; gpuTextureFormats[PIXELFORMAT_RG16UI] = 'rg16uint'; gpuTextureFormats[PIXELFORMAT_RG32I] = 'rg32sint'; gpuTextureFormats[PIXELFORMAT_RG32UI] = 'rg32uint'; -gpuTextureFormats[PIXELFORMAT_RGBA8I] = 'r8sint'; +gpuTextureFormats[PIXELFORMAT_RGBA8I] = 'rgba8sint'; gpuTextureFormats[PIXELFORMAT_RGBA8UI] = 'rgba8uint'; gpuTextureFormats[PIXELFORMAT_RGBA16I] = 'rgba16sint'; gpuTextureFormats[PIXELFORMAT_RGBA16UI] = 'rgba16uint'; From a9b19fc05825927d1085125917769d6aafcfe583 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Thu, 11 Jan 2024 17:25:12 -0800 Subject: [PATCH 11/43] Keep original textureDimensions order in shader-processor --- src/platform/graphics/shader-processor.js | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/platform/graphics/shader-processor.js b/src/platform/graphics/shader-processor.js index 70402742e2c..49c0225b774 100644 --- a/src/platform/graphics/shader-processor.js +++ b/src/platform/graphics/shader-processor.js @@ -28,23 +28,20 @@ const precisionQualifiers = new Set(['highp', 'mediump', 'lowp']); const shadowSamplers = new Set(['sampler2DShadow', 'samplerCubeShadow', 'sampler2DArrayShadow']); const textureDimensions = { sampler2D: TEXTUREDIMENSION_2D, + sampler3D: TEXTUREDIMENSION_3D, + samplerCube: TEXTUREDIMENSION_CUBE, + samplerCubeShadow: TEXTUREDIMENSION_CUBE, + sampler2DShadow: TEXTUREDIMENSION_2D, + sampler2DArray: TEXTUREDIMENSION_2D_ARRAY, + sampler2DArrayShadow: TEXTUREDIMENSION_2D_ARRAY, isampler2D: TEXTUREDIMENSION_2D, usampler2D: TEXTUREDIMENSION_2D, - sampler2DShadow: TEXTUREDIMENSION_2D, - - sampler3D: TEXTUREDIMENSION_3D, isampler3D: TEXTUREDIMENSION_3D, usampler3D: TEXTUREDIMENSION_3D, - - samplerCube: TEXTUREDIMENSION_CUBE, isamplerCube: TEXTUREDIMENSION_CUBE, usamplerCube: TEXTUREDIMENSION_CUBE, - samplerCubeShadow: TEXTUREDIMENSION_CUBE, - - sampler2DArray: TEXTUREDIMENSION_2D_ARRAY, isampler2DArray: TEXTUREDIMENSION_2D_ARRAY, - usampler2DArray: TEXTUREDIMENSION_2D_ARRAY, - sampler2DArrayShadow: TEXTUREDIMENSION_2D_ARRAY + usampler2DArray: TEXTUREDIMENSION_2D_ARRAY }; class UniformLine { From 226f8200a8ede172d89d1371d16b77ffc78599a2 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Thu, 11 Jan 2024 17:44:51 -0800 Subject: [PATCH 12/43] Revert uniform-buffer with minimal additions --- src/platform/graphics/uniform-buffer.js | 221 ++++++++++++++++++++---- 1 file changed, 186 insertions(+), 35 deletions(-) diff --git a/src/platform/graphics/uniform-buffer.js b/src/platform/graphics/uniform-buffer.js index a4f7b5de827..802702aa88f 100644 --- a/src/platform/graphics/uniform-buffer.js +++ b/src/platform/graphics/uniform-buffer.js @@ -1,35 +1,197 @@ import { Debug } from '../../core/debug.js'; import { uniformTypeToName, - UNIFORMTYPE_MAT2, UNIFORMTYPE_MAT3, uniformTypeToStorage, TYPE_INT32, TYPE_FLOAT32, TYPE_UINT32 + UNIFORMTYPE_INT, UNIFORMTYPE_FLOAT, UNIFORMTYPE_VEC2, UNIFORMTYPE_VEC3, + UNIFORMTYPE_VEC4, UNIFORMTYPE_IVEC2, UNIFORMTYPE_IVEC3, UNIFORMTYPE_IVEC4, + UNIFORMTYPE_FLOATARRAY, UNIFORMTYPE_VEC2ARRAY, UNIFORMTYPE_VEC3ARRAY, + UNIFORMTYPE_MAT2, UNIFORMTYPE_MAT3, UNIFORMTYPE_UINT, UNIFORMTYPE_UVEC2, UNIFORMTYPE_UVEC3, UNIFORMTYPE_UVEC4, UNIFORMTYPE_INTARRAY, UNIFORMTYPE_UINTARRAY, UNIFORMTYPE_BOOLARRAY, UNIFORMTYPE_IVEC2ARRAY, UNIFORMTYPE_IVEC3ARRAY, UNIFORMTYPE_UVEC2ARRAY, UNIFORMTYPE_UVEC3ARRAY, UNIFORMTYPE_BVEC2ARRAY, UNIFORMTYPE_BVEC3ARRAY } from './constants.js'; import { DynamicBufferAllocation } from './dynamic-buffers.js'; -function set(dst, value, offset, numComponents, stride = numComponents) { - for (let i = 0; i < numComponents; i++) { - dst[offset + i * stride] = value[i]; - } -} - -function setArray(dst, value, offset, numComponents, count, componentStride = numComponents, arrayStride = componentStride) { - for (let i = 0; i < count; i++) { - for (let j = 0; j < numComponents; j++) { - dst[offset + i * arrayStride + j] = value[i * componentStride + j]; - } - } -} - // Uniform buffer set functions - only implemented for types for which the default // array to buffer copy does not work, or could be slower. const _updateFunctions = []; + +_updateFunctions[UNIFORMTYPE_FLOAT] = function (uniformBuffer, value, offset) { + const dst = uniformBuffer.storageFloat32; + dst[offset] = value; +}; + +_updateFunctions[UNIFORMTYPE_VEC2] = (uniformBuffer, value, offset) => { + const dst = uniformBuffer.storageFloat32; + dst[offset] = value[0]; + dst[offset + 1] = value[1]; +}; + +_updateFunctions[UNIFORMTYPE_VEC3] = (uniformBuffer, value, offset) => { + const dst = uniformBuffer.storageFloat32; + dst[offset] = value[0]; + dst[offset + 1] = value[1]; + dst[offset + 2] = value[2]; +}; + +_updateFunctions[UNIFORMTYPE_VEC4] = (uniformBuffer, value, offset) => { + const dst = uniformBuffer.storageFloat32; + dst[offset] = value[0]; + dst[offset + 1] = value[1]; + dst[offset + 2] = value[2]; + dst[offset + 3] = value[3]; +}; + +_updateFunctions[UNIFORMTYPE_INT] = function (uniformBuffer, value, offset) { + const dst = uniformBuffer.storageInt32; + dst[offset] = value; +}; + +_updateFunctions[UNIFORMTYPE_IVEC2] = function (uniformBuffer, value, offset) { + const dst = uniformBuffer.storageInt32; + dst[offset] = value[0]; + dst[offset + 1] = value[1]; +}; + +_updateFunctions[UNIFORMTYPE_IVEC3] = function (uniformBuffer, value, offset) { + const dst = uniformBuffer.storageInt32; + dst[offset] = value[0]; + dst[offset + 1] = value[1]; + dst[offset + 2] = value[2]; +}; + +_updateFunctions[UNIFORMTYPE_IVEC4] = function (uniformBuffer, value, offset) { + const dst = uniformBuffer.storageInt32; + dst[offset] = value[0]; + dst[offset + 1] = value[1]; + dst[offset + 2] = value[2]; + dst[offset + 3] = value[3]; +}; + // convert from continuous array to vec2[3] with padding to vec4[2] _updateFunctions[UNIFORMTYPE_MAT2] = (uniformBuffer, value, offset) => { - setArray(uniformBuffer.storageFloat32, value, offset, 2, 2, 2, 4); + const dst = uniformBuffer.storageFloat32; + dst[offset] = value[0]; + dst[offset + 1] = value[1]; + + dst[offset + 4] = value[2]; + dst[offset + 5] = value[3]; + + dst[offset + 8] = value[4]; + dst[offset + 9] = value[5]; }; // convert from continuous array to vec3[3] with padding to vec4[3] _updateFunctions[UNIFORMTYPE_MAT3] = (uniformBuffer, value, offset) => { - setArray(uniformBuffer.storageFloat32, value, offset, 3, 3, 3, 4); + const dst = uniformBuffer.storageFloat32; + dst[offset] = value[0]; + dst[offset + 1] = value[1]; + dst[offset + 2] = value[2]; + + dst[offset + 4] = value[3]; + dst[offset + 5] = value[4]; + dst[offset + 6] = value[5]; + + dst[offset + 8] = value[6]; + dst[offset + 9] = value[7]; + dst[offset + 10] = value[8]; +}; + +_updateFunctions[UNIFORMTYPE_FLOATARRAY] = function (uniformBuffer, value, offset, count) { + const dst = uniformBuffer.storageFloat32; + for (let i = 0; i < count; i++) { + dst[offset + i * 4] = value[i]; + } +}; + +_updateFunctions[UNIFORMTYPE_VEC2ARRAY] = (uniformBuffer, value, offset, count) => { + const dst = uniformBuffer.storageFloat32; + for (let i = 0; i < count; i++) { + dst[offset + i * 4] = value[i * 2]; + dst[offset + i * 4 + 1] = value[i * 2 + 1]; + } +}; + +_updateFunctions[UNIFORMTYPE_VEC3ARRAY] = (uniformBuffer, value, offset, count) => { + const dst = uniformBuffer.storageFloat32; + for (let i = 0; i < count; i++) { + dst[offset + i * 4] = value[i * 3]; + dst[offset + i * 4 + 1] = value[i * 3 + 1]; + dst[offset + i * 4 + 2] = value[i * 3 + 2]; + } +}; + +_updateFunctions[UNIFORMTYPE_UINT] = (uniformBuffer, value, offset, count) => { + const dst = uniformBuffer.storageUint32; + dst[offset] = value; +}; + +_updateFunctions[UNIFORMTYPE_UVEC2] = (uniformBuffer, value, offset, count) => { + const dst = uniformBuffer.storageUint32; + dst[offset] = value[0]; + dst[offset + 1] = value[1]; +}; + +_updateFunctions[UNIFORMTYPE_UVEC3] = (uniformBuffer, value, offset, count) => { + const dst = uniformBuffer.storageUint32; + dst[offset] = value[0]; + dst[offset + 1] = value[1]; + dst[offset + 2] = value[2]; +}; + +_updateFunctions[UNIFORMTYPE_UVEC4] = (uniformBuffer, value, offset, count) => { + const dst = uniformBuffer.storageUint32; + dst[offset] = value[0]; + dst[offset + 1] = value[1]; + dst[offset + 2] = value[2]; + dst[offset + 3] = value[3]; +}; + +_updateFunctions[UNIFORMTYPE_INTARRAY] = function (uniformBuffer, value, offset, count) { + const dst = uniformBuffer.storageInt32; + for (let i = 0; i < count; i++) { + dst[offset + i * 4] = value[i]; + } +}; +_updateFunctions[UNIFORMTYPE_BOOLARRAY] = _updateFunctions[UNIFORMTYPE_INTARRAY]; + +_updateFunctions[UNIFORMTYPE_UINTARRAY] = function (uniformBuffer, value, offset, count) { + const dst = uniformBuffer.storageUint32; + for (let i = 0; i < count; i++) { + dst[offset + i * 4] = value[i]; + } +}; + +_updateFunctions[UNIFORMTYPE_IVEC2ARRAY] = (uniformBuffer, value, offset, count) => { + const dst = uniformBuffer.storageInt32; + for (let i = 0; i < count; i++) { + dst[offset + i * 4] = value[i * 2]; + dst[offset + i * 4 + 1] = value[i * 2 + 1]; + } +}; +_updateFunctions[UNIFORMTYPE_BVEC2ARRAY] = _updateFunctions[UNIFORMTYPE_IVEC2ARRAY]; + +_updateFunctions[UNIFORMTYPE_UVEC2ARRAY] = (uniformBuffer, value, offset, count) => { + const dst = uniformBuffer.storageUint32; + for (let i = 0; i < count; i++) { + dst[offset + i * 4] = value[i * 2]; + dst[offset + i * 4 + 1] = value[i * 2 + 1]; + } +}; + +_updateFunctions[UNIFORMTYPE_IVEC3ARRAY] = (uniformBuffer, value, offset, count) => { + const dst = uniformBuffer.storageInt32; + for (let i = 0; i < count; i++) { + dst[offset + i * 4] = value[i * 3]; + dst[offset + i * 4 + 1] = value[i * 3 + 1]; + dst[offset + i * 4 + 2] = value[i * 3 + 2]; + } +}; +_updateFunctions[UNIFORMTYPE_BVEC3ARRAY] = _updateFunctions[UNIFORMTYPE_IVEC3ARRAY]; + +_updateFunctions[UNIFORMTYPE_UVEC3ARRAY] = (uniformBuffer, value, offset, count) => { + const dst = uniformBuffer.storageUint32; + for (let i = 0; i < count; i++) { + dst[offset + i * 4] = value[i * 3]; + dst[offset + i * 4 + 1] = value[i * 3 + 1]; + dst[offset + i * 4 + 2] = value[i * 3 + 2]; + } }; /** @@ -46,13 +208,13 @@ class UniformBuffer { /** @type {DynamicBufferAllocation} */ allocation; - /** @type {Float32Array | undefined} */ + /** @type {Float32Array} */ storageFloat32; - /** @type {Int32Array | undefined} */ + /** @type {Int32Array} */ storageInt32; - /** @type {Uint32Array | undefined} */ + /** @type {Uint32Array} */ storageUint32; /** @@ -148,22 +310,12 @@ class UniformBuffer { const value = uniformFormat.scopeId.value; if (value !== null && value !== undefined) { - const storageType = uniformTypeToStorage[uniformFormat.type]; - const dst = storageType === TYPE_INT32 ? this.storageInt32 : - storageType === TYPE_UINT32 ? this.storageUint32 : this.storageFloat32; - - if (!dst) { - Debug.error(`Uniform buffer storage is not allocated for type ${uniformTypeToName[uniformFormat.type]}`); - return; - } const updateFunction = _updateFunctions[uniformFormat.updateType]; if (updateFunction) { - updateFunction(this, value, offset, uniformFormat.numComponents, uniformFormat.count); - } else if (uniformFormat.isArrayType) { - setArray(dst, value, offset, uniformFormat.numComponents, uniformFormat.count); + updateFunction(this, value, offset, uniformFormat.count); } else { - set(dst, value, offset, uniformFormat.numComponents); + this.storageFloat32.set(value, offset); } } else { Debug.warnOnce(`Value was not set when assigning to uniform [${uniformFormat.name}]` + @@ -211,9 +363,8 @@ class UniformBuffer { // Upload the new data this.impl.unlock(this); } else { - this.storageFloat32 = undefined; - this.storageInt32 = undefined; - this.storageUint32 = undefined; + this.storageFloat32 = null; + this.storageInt32 = null; } } } From 5d4a7c5ba5caa3f559af4a0b83815c019261d66e Mon Sep 17 00:00:00 2001 From: Liam Don Date: Fri, 12 Jan 2024 10:13:40 -0800 Subject: [PATCH 13/43] Do not allow changing integer texture mipmap/filter properties, log warning --- src/platform/graphics/texture.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/platform/graphics/texture.js b/src/platform/graphics/texture.js index 2a9eda05d82..5d61c393700 100644 --- a/src/platform/graphics/texture.js +++ b/src/platform/graphics/texture.js @@ -391,8 +391,12 @@ class Texture { */ set minFilter(v) { if (this._minFilter !== v) { - this._minFilter = v; - this.propertyChanged(1); + if (isIntegerPixelFormat(this._format)) { + Debug.warn("Texture#minFilter: minFilter property cannot be changed on an integer texture, will remain FILTER_NEAREST", this); + } else { + this._minFilter = v; + this.propertyChanged(1); + } } } @@ -410,8 +414,12 @@ class Texture { */ set magFilter(v) { if (this._magFilter !== v) { - this._magFilter = v; - this.propertyChanged(2); + if (isIntegerPixelFormat(this._format)) { + Debug.warn("Texture#magFilter: magFilter property cannot be changed on an integer texture, will remain FILTER_NEAREST", this); + } else { + this._magFilter = v; + this.propertyChanged(2); + } } } @@ -549,10 +557,13 @@ class Texture { */ set mipmaps(v) { if (this._mipmaps !== v) { - this._mipmaps = v; if (this.device.isWebGPU) { Debug.warn("Texture#mipmaps: mipmap property is currently not allowed to be changed on WebGPU, create the texture appropriately.", this); + } else if (isIntegerPixelFormat(this._format)) { + Debug.warn("Texture#mipmaps: mipmap property cannot be changed on an integer texture, will remain false", this); + } else { + this._mipmaps = v; } if (v) this._needsMipmapsUpload = true; From 8a507bff0189bd441b65ddcd7d2caf07d7fc21a6 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Fri, 12 Jan 2024 10:15:36 -0800 Subject: [PATCH 14/43] Revert formatting changes to compressed formats --- src/platform/graphics/constants.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/platform/graphics/constants.js b/src/platform/graphics/constants.js index 0201960c8b9..bbf5c746237 100644 --- a/src/platform/graphics/constants.js +++ b/src/platform/graphics/constants.js @@ -770,19 +770,19 @@ export const pixelFormatInfo = new Map([ [PIXELFORMAT_BGRA8, { name: 'BGRA8', size: 4 }], // compressed formats - [PIXELFORMAT_DXT1, { name: 'DXT1', blockSize: 8 }], - [PIXELFORMAT_DXT3, { name: 'DXT3', blockSize: 16 }], - [PIXELFORMAT_DXT5, { name: 'DXT5', blockSize: 16 }], - [PIXELFORMAT_ETC1, { name: 'ETC1', blockSize: 8 }], - [PIXELFORMAT_ETC2_RGB, { name: 'ETC2_RGB', blockSize: 8 }], - [PIXELFORMAT_ETC2_RGBA, { name: 'ETC2_RGBA', blockSize: 16 }], - [PIXELFORMAT_PVRTC_2BPP_RGB_1, { name: 'PVRTC_2BPP_RGB_1', blockSize: 8 }], + [PIXELFORMAT_DXT1, { name: 'DXT1', blockSize: 8 }], + [PIXELFORMAT_DXT3, { name: 'DXT3', blockSize: 16 }], + [PIXELFORMAT_DXT5, { name: 'DXT5', blockSize: 16 }], + [PIXELFORMAT_ETC1, { name: 'ETC1', blockSize: 8 }], + [PIXELFORMAT_ETC2_RGB, { name: 'ETC2_RGB', blockSize: 8 }], + [PIXELFORMAT_ETC2_RGBA, { name: 'ETC2_RGBA', blockSize: 16 }], + [PIXELFORMAT_PVRTC_2BPP_RGB_1, { name: 'PVRTC_2BPP_RGB_1', blockSize: 8 }], [PIXELFORMAT_PVRTC_2BPP_RGBA_1, { name: 'PVRTC_2BPP_RGBA_1', blockSize: 8 }], - [PIXELFORMAT_PVRTC_4BPP_RGB_1, { name: 'PVRTC_4BPP_RGB_1', blockSize: 8 }], + [PIXELFORMAT_PVRTC_4BPP_RGB_1, { name: 'PVRTC_4BPP_RGB_1', blockSize: 8 }], [PIXELFORMAT_PVRTC_4BPP_RGBA_1, { name: 'PVRTC_4BPP_RGBA_1', blockSize: 8 }], - [PIXELFORMAT_ASTC_4x4, { name: 'ASTC_4x4', blockSize: 16 }], - [PIXELFORMAT_ATC_RGB, { name: 'ATC_RGB', blockSize: 8 }], - [PIXELFORMAT_ATC_RGBA, { name: 'ATC_RGBA', blockSize: 16 }], + [PIXELFORMAT_ASTC_4x4, { name: 'ASTC_4x4', blockSize: 16 }], + [PIXELFORMAT_ATC_RGB, { name: 'ATC_RGB', blockSize: 8 }], + [PIXELFORMAT_ATC_RGBA, { name: 'ATC_RGBA', blockSize: 16 }], // uncompressed integer formats (Not supported on WebGL1) [PIXELFORMAT_R8I, { name: 'R8I', size: 1, isInt: true }], From b06dd170f5bbd3fccd4aaa34121d6608f1a30da2 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Fri, 12 Jan 2024 10:16:38 -0800 Subject: [PATCH 15/43] Revert previous comment change --- src/platform/graphics/constants.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/graphics/constants.js b/src/platform/graphics/constants.js index bbf5c746237..3ef1743e78f 100644 --- a/src/platform/graphics/constants.js +++ b/src/platform/graphics/constants.js @@ -169,7 +169,7 @@ export const BUFFER_STREAM = 2; /** * The data store contents will be modified repeatedly on the GPU and used many times. Optimal for - * transform feedback usage (not supported by WebGL1). + * transform feedback usage (WebGL2 only). * * @type {number} */ From de39dbfd00b974c2f59c91638a93f40489f149ee Mon Sep 17 00:00:00 2001 From: Liam Don Date: Fri, 12 Jan 2024 11:34:01 -0800 Subject: [PATCH 16/43] Keep original order of pcUniformType in webgl-graphics-device --- .../graphics/webgl/webgl-graphics-device.js | 47 +++++++++---------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/src/platform/graphics/webgl/webgl-graphics-device.js b/src/platform/graphics/webgl/webgl-graphics-device.js index e9ab3ac61cf..24c89c33335 100644 --- a/src/platform/graphics/webgl/webgl-graphics-device.js +++ b/src/platform/graphics/webgl/webgl-graphics-device.js @@ -550,46 +550,43 @@ class WebglGraphicsDevice extends GraphicsDevice { ]; this.pcUniformType = {}; - this.pcUniformType[gl.FLOAT] = UNIFORMTYPE_FLOAT; - this.pcUniformType[gl.INT] = UNIFORMTYPE_INT; + this.pcUniformType[gl.BOOL] = UNIFORMTYPE_BOOL; + this.pcUniformType[gl.INT] = UNIFORMTYPE_INT; + this.pcUniformType[gl.FLOAT] = UNIFORMTYPE_FLOAT; + this.pcUniformType[gl.FLOAT_VEC2] = UNIFORMTYPE_VEC2; + this.pcUniformType[gl.FLOAT_VEC3] = UNIFORMTYPE_VEC3; + this.pcUniformType[gl.FLOAT_VEC4] = UNIFORMTYPE_VEC4; + this.pcUniformType[gl.INT_VEC2] = UNIFORMTYPE_IVEC2; + this.pcUniformType[gl.INT_VEC3] = UNIFORMTYPE_IVEC3; + this.pcUniformType[gl.INT_VEC4] = UNIFORMTYPE_IVEC4; + this.pcUniformType[gl.BOOL_VEC2] = UNIFORMTYPE_BVEC2; + this.pcUniformType[gl.BOOL_VEC3] = UNIFORMTYPE_BVEC3; + this.pcUniformType[gl.BOOL_VEC4] = UNIFORMTYPE_BVEC4; + this.pcUniformType[gl.FLOAT_MAT2] = UNIFORMTYPE_MAT2; + this.pcUniformType[gl.FLOAT_MAT3] = UNIFORMTYPE_MAT3; + this.pcUniformType[gl.FLOAT_MAT4] = UNIFORMTYPE_MAT4; + this.pcUniformType[gl.SAMPLER_2D] = UNIFORMTYPE_TEXTURE2D; + this.pcUniformType[gl.SAMPLER_CUBE] = UNIFORMTYPE_TEXTURECUBE; this.pcUniformType[gl.UNSIGNED_INT] = UNIFORMTYPE_UINT; - this.pcUniformType[gl.BOOL] = UNIFORMTYPE_BOOL; - - this.pcUniformType[gl.FLOAT_VEC2] = UNIFORMTYPE_VEC2; - this.pcUniformType[gl.INT_VEC2] = UNIFORMTYPE_IVEC2; this.pcUniformType[gl.UNSIGNED_INT_VEC2] = UNIFORMTYPE_UVEC2; - this.pcUniformType[gl.BOOL_VEC2] = UNIFORMTYPE_BVEC2; - - this.pcUniformType[gl.FLOAT_VEC3] = UNIFORMTYPE_VEC3; - this.pcUniformType[gl.INT_VEC3] = UNIFORMTYPE_IVEC3; this.pcUniformType[gl.UNSIGNED_INT_VEC3] = UNIFORMTYPE_UVEC3; - this.pcUniformType[gl.BOOL_VEC3] = UNIFORMTYPE_BVEC3; - - this.pcUniformType[gl.FLOAT_VEC4] = UNIFORMTYPE_VEC4; - this.pcUniformType[gl.INT_VEC4] = UNIFORMTYPE_IVEC4; this.pcUniformType[gl.UNSIGNED_INT_VEC4] = UNIFORMTYPE_UVEC4; - this.pcUniformType[gl.BOOL_VEC4] = UNIFORMTYPE_BVEC4; - - this.pcUniformType[gl.FLOAT_MAT2] = UNIFORMTYPE_MAT2; - this.pcUniformType[gl.FLOAT_MAT3] = UNIFORMTYPE_MAT3; - this.pcUniformType[gl.FLOAT_MAT4] = UNIFORMTYPE_MAT4; - this.pcUniformType[gl.SAMPLER_2D] = UNIFORMTYPE_TEXTURE2D; - this.pcUniformType[gl.SAMPLER_CUBE] = UNIFORMTYPE_TEXTURECUBE; if (this.isWebGL2) { + this.pcUniformType[gl.SAMPLER_2D_SHADOW] = UNIFORMTYPE_TEXTURE2D_SHADOW; + this.pcUniformType[gl.SAMPLER_CUBE_SHADOW] = UNIFORMTYPE_TEXTURECUBE_SHADOW; + this.pcUniformType[gl.SAMPLER_2D_ARRAY] = UNIFORMTYPE_TEXTURE2D_ARRAY; + this.pcUniformType[gl.SAMPLER_3D] = UNIFORMTYPE_TEXTURE3D; + this.pcUniformType[gl.INT_SAMPLER_2D] = UNIFORMTYPE_ITEXTURE2D; this.pcUniformType[gl.UNSIGNED_INT_SAMPLER_2D] = UNIFORMTYPE_UTEXTURE2D; - this.pcUniformType[gl.SAMPLER_2D_SHADOW] = UNIFORMTYPE_TEXTURE2D_SHADOW; this.pcUniformType[gl.INT_SAMPLER_CUBE] = UNIFORMTYPE_ITEXTURECUBE; this.pcUniformType[gl.UNSIGNED_INT_SAMPLER_2D] = UNIFORMTYPE_UTEXTURECUBE; - this.pcUniformType[gl.SAMPLER_CUBE_SHADOW] = UNIFORMTYPE_TEXTURECUBE_SHADOW; - this.pcUniformType[gl.SAMPLER_3D] = UNIFORMTYPE_TEXTURE3D; this.pcUniformType[gl.INT_SAMPLER_3D] = UNIFORMTYPE_ITEXTURE3D; this.pcUniformType[gl.UNSIGNED_INT_SAMPLER_3D] = UNIFORMTYPE_UTEXTURE3D; - this.pcUniformType[gl.SAMPLER_2D_ARRAY] = UNIFORMTYPE_TEXTURE2D_ARRAY; this.pcUniformType[gl.INT_SAMPLER_2D_ARRAY] = UNIFORMTYPE_ITEXTURE2D_ARRAY; this.pcUniformType[gl.UNSIGNED_INT_SAMPLER_2D_ARRAY] = UNIFORMTYPE_UTEXTURE2D_ARRAY; } From 3f757529bfd286efe954862655cb37320b9f4e87 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Fri, 12 Jan 2024 11:53:39 -0800 Subject: [PATCH 17/43] Keep original order in webgl-graphics-device --- .../graphics/webgl/webgl-graphics-device.js | 129 +++++++++--------- 1 file changed, 63 insertions(+), 66 deletions(-) diff --git a/src/platform/graphics/webgl/webgl-graphics-device.js b/src/platform/graphics/webgl/webgl-graphics-device.js index 24c89c33335..99fb5b622cb 100644 --- a/src/platform/graphics/webgl/webgl-graphics-device.js +++ b/src/platform/graphics/webgl/webgl-graphics-device.js @@ -600,26 +600,19 @@ class WebglGraphicsDevice extends GraphicsDevice { let scopeX, scopeY, scopeZ, scopeW; let uniformValue; this.commitFunction = []; - this.commitFunction[UNIFORMTYPE_FLOAT] = function (uniform, value) { - if (uniform.value !== value) { - gl.uniform1f(uniform.locationId, value); - uniform.value = value; - } - }; - this.commitFunction[UNIFORMTYPE_INT] = function (uniform, value) { + this.commitFunction[UNIFORMTYPE_BOOL] = function (uniform, value) { if (uniform.value !== value) { gl.uniform1i(uniform.locationId, value); uniform.value = value; } }; - this.commitFunction[UNIFORMTYPE_UINT] = function (uniform, value) { + this.commitFunction[UNIFORMTYPE_INT] = this.commitFunction[UNIFORMTYPE_BOOL]; + this.commitFunction[UNIFORMTYPE_FLOAT] = function (uniform, value) { if (uniform.value !== value) { - gl.uniform1ui(uniform.locationId, value); + gl.uniform1f(uniform.locationId, value); uniform.value = value; } }; - this.commitFunction[UNIFORMTYPE_BOOL] = this.commitFunction[UNIFORMTYPE_INT]; - this.commitFunction[UNIFORMTYPE_VEC2] = function (uniform, value) { uniformValue = uniform.value; scopeX = value[0]; @@ -630,41 +623,44 @@ class WebglGraphicsDevice extends GraphicsDevice { uniformValue[1] = scopeY; } }; - this.commitFunction[UNIFORMTYPE_IVEC2] = function (uniform, value) { + this.commitFunction[UNIFORMTYPE_VEC3] = function (uniform, value) { uniformValue = uniform.value; scopeX = value[0]; scopeY = value[1]; - if (uniformValue[0] !== scopeX || uniformValue[1] !== scopeY) { - gl.uniform2iv(uniform.locationId, value); + scopeZ = value[2]; + if (uniformValue[0] !== scopeX || uniformValue[1] !== scopeY || uniformValue[2] !== scopeZ) { + gl.uniform3fv(uniform.locationId, value); uniformValue[0] = scopeX; uniformValue[1] = scopeY; + uniformValue[2] = scopeZ; } }; - this.commitFunction[UNIFORMTYPE_UVEC2] = function (uniform, value) { + this.commitFunction[UNIFORMTYPE_VEC4] = function (uniform, value) { uniformValue = uniform.value; scopeX = value[0]; scopeY = value[1]; - if (uniformValue[0] !== scopeX || uniformValue[1] !== scopeY) { - gl.uniform2uiv(uniform.locationId, value); + scopeZ = value[2]; + scopeW = value[3]; + if (uniformValue[0] !== scopeX || uniformValue[1] !== scopeY || uniformValue[2] !== scopeZ || uniformValue[3] !== scopeW) { + gl.uniform4fv(uniform.locationId, value); uniformValue[0] = scopeX; uniformValue[1] = scopeY; + uniformValue[2] = scopeZ; + uniformValue[3] = scopeW; } }; - this.commitFunction[UNIFORMTYPE_BVEC2] = this.commitFunction[UNIFORMTYPE_IVEC2]; - - this.commitFunction[UNIFORMTYPE_VEC3] = function (uniform, value) { + this.commitFunction[UNIFORMTYPE_IVEC2] = function (uniform, value) { uniformValue = uniform.value; scopeX = value[0]; scopeY = value[1]; - scopeZ = value[2]; - if (uniformValue[0] !== scopeX || uniformValue[1] !== scopeY || uniformValue[2] !== scopeZ) { - gl.uniform3fv(uniform.locationId, value); + if (uniformValue[0] !== scopeX || uniformValue[1] !== scopeY) { + gl.uniform2iv(uniform.locationId, value); uniformValue[0] = scopeX; uniformValue[1] = scopeY; - uniformValue[2] = scopeZ; } }; - this.commitFunction[UNIFORMTYPE_IVEC3] = function (uniform, value) { + this.commitFunction[UNIFORMTYPE_BVEC2] = this.commitFunction[UNIFORMTYPE_IVEC2]; + this.commitFunction[UNIFORMTYPE_IVEC3] = function (uniform, value) { uniformValue = uniform.value; scopeX = value[0]; scopeY = value[1]; @@ -676,46 +672,70 @@ class WebglGraphicsDevice extends GraphicsDevice { uniformValue[2] = scopeZ; } }; - this.commitFunction[UNIFORMTYPE_UVEC3] = function (uniform, value) { + this.commitFunction[UNIFORMTYPE_BVEC3] = this.commitFunction[UNIFORMTYPE_IVEC3]; + this.commitFunction[UNIFORMTYPE_IVEC4] = function (uniform, value) { uniformValue = uniform.value; scopeX = value[0]; scopeY = value[1]; scopeZ = value[2]; - if (uniformValue[0] !== scopeX || uniformValue[1] !== scopeY || uniformValue[2] !== scopeZ) { - gl.uniform3uiv(uniform.locationId, value); + scopeW = value[3]; + if (uniformValue[0] !== scopeX || uniformValue[1] !== scopeY || uniformValue[2] !== scopeZ || uniformValue[3] !== scopeW) { + gl.uniform4iv(uniform.locationId, value); uniformValue[0] = scopeX; uniformValue[1] = scopeY; uniformValue[2] = scopeZ; + uniformValue[3] = scopeW; } }; - this.commitFunction[UNIFORMTYPE_BVEC3] = this.commitFunction[UNIFORMTYPE_IVEC3]; + this.commitFunction[UNIFORMTYPE_BVEC4] = this.commitFunction[UNIFORMTYPE_IVEC4]; + this.commitFunction[UNIFORMTYPE_MAT2] = function (uniform, value) { + gl.uniformMatrix2fv(uniform.locationId, false, value); + }; + this.commitFunction[UNIFORMTYPE_MAT3] = function (uniform, value) { + gl.uniformMatrix3fv(uniform.locationId, false, value); + }; + this.commitFunction[UNIFORMTYPE_MAT4] = function (uniform, value) { + gl.uniformMatrix4fv(uniform.locationId, false, value); + }; + this.commitFunction[UNIFORMTYPE_FLOATARRAY] = function (uniform, value) { + gl.uniform1fv(uniform.locationId, value); + }; + this.commitFunction[UNIFORMTYPE_VEC2ARRAY] = function (uniform, value) { + gl.uniform2fv(uniform.locationId, value); + }; + this.commitFunction[UNIFORMTYPE_VEC3ARRAY] = function (uniform, value) { + gl.uniform3fv(uniform.locationId, value); + }; + this.commitFunction[UNIFORMTYPE_VEC4ARRAY] = function (uniform, value) { + gl.uniform4fv(uniform.locationId, value); + }; - this.commitFunction[UNIFORMTYPE_VEC4] = function (uniform, value) { + this.commitFunction[UNIFORMTYPE_UINT] = function (uniform, value) { + if (uniform.value !== value) { + gl.uniform1ui(uniform.locationId, value); + uniform.value = value; + } + }; + this.commitFunction[UNIFORMTYPE_UVEC2] = function (uniform, value) { uniformValue = uniform.value; scopeX = value[0]; scopeY = value[1]; - scopeZ = value[2]; - scopeW = value[3]; - if (uniformValue[0] !== scopeX || uniformValue[1] !== scopeY || uniformValue[2] !== scopeZ || uniformValue[3] !== scopeW) { - gl.uniform4fv(uniform.locationId, value); + if (uniformValue[0] !== scopeX || uniformValue[1] !== scopeY) { + gl.uniform2uiv(uniform.locationId, value); uniformValue[0] = scopeX; uniformValue[1] = scopeY; - uniformValue[2] = scopeZ; - uniformValue[3] = scopeW; } }; - this.commitFunction[UNIFORMTYPE_IVEC4] = function (uniform, value) { + this.commitFunction[UNIFORMTYPE_UVEC3] = function (uniform, value) { uniformValue = uniform.value; scopeX = value[0]; scopeY = value[1]; scopeZ = value[2]; - scopeW = value[3]; - if (uniformValue[0] !== scopeX || uniformValue[1] !== scopeY || uniformValue[2] !== scopeZ || uniformValue[3] !== scopeW) { - gl.uniform4iv(uniform.locationId, value); + if (uniformValue[0] !== scopeX || uniformValue[1] !== scopeY || uniformValue[2] !== scopeZ) { + gl.uniform3uiv(uniform.locationId, value); uniformValue[0] = scopeX; uniformValue[1] = scopeY; uniformValue[2] = scopeZ; - uniformValue[3] = scopeW; } }; this.commitFunction[UNIFORMTYPE_UVEC4] = function (uniform, value) { @@ -732,21 +752,7 @@ class WebglGraphicsDevice extends GraphicsDevice { uniformValue[3] = scopeW; } }; - this.commitFunction[UNIFORMTYPE_BVEC4] = this.commitFunction[UNIFORMTYPE_IVEC4]; - this.commitFunction[UNIFORMTYPE_MAT2] = function (uniform, value) { - gl.uniformMatrix2fv(uniform.locationId, false, value); - }; - this.commitFunction[UNIFORMTYPE_MAT3] = function (uniform, value) { - gl.uniformMatrix3fv(uniform.locationId, false, value); - }; - this.commitFunction[UNIFORMTYPE_MAT4] = function (uniform, value) { - gl.uniformMatrix4fv(uniform.locationId, false, value); - }; - - this.commitFunction[UNIFORMTYPE_FLOATARRAY] = function (uniform, value) { - gl.uniform1fv(uniform.locationId, value); - }; this.commitFunction[UNIFORMTYPE_INTARRAY] = function (uniform, value) { gl.uniform1iv(uniform.locationId, value); }; @@ -755,9 +761,6 @@ class WebglGraphicsDevice extends GraphicsDevice { }; this.commitFunction[UNIFORMTYPE_BOOLARRAY] = this.commitFunction[UNIFORMTYPE_INTARRAY]; - this.commitFunction[UNIFORMTYPE_VEC2ARRAY] = function (uniform, value) { - gl.uniform2fv(uniform.locationId, value); - }; this.commitFunction[UNIFORMTYPE_IVEC2ARRAY] = function (uniform, value) { gl.uniform2iv(uniform.locationId, value); }; @@ -766,27 +769,21 @@ class WebglGraphicsDevice extends GraphicsDevice { }; this.commitFunction[UNIFORMTYPE_BVEC2ARRAY] = this.commitFunction[UNIFORMTYPE_IVEC2ARRAY]; - this.commitFunction[UNIFORMTYPE_VEC3ARRAY] = function (uniform, value) { - gl.uniform3fv(uniform.locationId, value); - }; this.commitFunction[UNIFORMTYPE_IVEC3ARRAY] = function (uniform, value) { gl.uniform3iv(uniform.locationId, value); }; this.commitFunction[UNIFORMTYPE_UVEC3ARRAY] = function (uniform, value) { gl.uniform3uiv(uniform.locationId, value); }; - this.commitFunction[UNIFORMTYPE_BVEC3ARRAY] = this.commitFunction[UNIFORMTYPE_IVEC2ARRAY]; + this.commitFunction[UNIFORMTYPE_BVEC3ARRAY] = this.commitFunction[UNIFORMTYPE_IVEC3ARRAY]; - this.commitFunction[UNIFORMTYPE_VEC4ARRAY] = function (uniform, value) { - gl.uniform4fv(uniform.locationId, value); - }; this.commitFunction[UNIFORMTYPE_IVEC4ARRAY] = function (uniform, value) { gl.uniform4iv(uniform.locationId, value); }; this.commitFunction[UNIFORMTYPE_UVEC4ARRAY] = function (uniform, value) { gl.uniform4uiv(uniform.locationId, value); }; - this.commitFunction[UNIFORMTYPE_BVEC4ARRAY] = this.commitFunction[UNIFORMTYPE_IVEC2ARRAY]; + this.commitFunction[UNIFORMTYPE_BVEC4ARRAY] = this.commitFunction[UNIFORMTYPE_IVEC4ARRAY]; this.commitFunction[UNIFORMTYPE_MAT4ARRAY] = function (uniform, value) { gl.uniformMatrix4fv(uniform.locationId, false, value); From 8db0a1e840290035111925c89645042cea356c95 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Fri, 12 Jan 2024 11:55:04 -0800 Subject: [PATCH 18/43] Minimize import diff --- src/platform/graphics/webgl/webgl-graphics-device.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/graphics/webgl/webgl-graphics-device.js b/src/platform/graphics/webgl/webgl-graphics-device.js index 99fb5b622cb..69c299580e0 100644 --- a/src/platform/graphics/webgl/webgl-graphics-device.js +++ b/src/platform/graphics/webgl/webgl-graphics-device.js @@ -22,11 +22,11 @@ import { UNIFORMTYPE_ITEXTURECUBE, UNIFORMTYPE_UTEXTURECUBE, UNIFORMTYPE_ITEXTURE3D, UNIFORMTYPE_UTEXTURE3D, UNIFORMTYPE_ITEXTURE2D_ARRAY, UNIFORMTYPE_UTEXTURE2D_ARRAY, UNIFORMTYPE_INTARRAY, UNIFORMTYPE_UINTARRAY, UNIFORMTYPE_BOOLARRAY, UNIFORMTYPE_IVEC2ARRAY, UNIFORMTYPE_BVEC2ARRAY, UNIFORMTYPE_UVEC2ARRAY, UNIFORMTYPE_IVEC3ARRAY, UNIFORMTYPE_BVEC3ARRAY, UNIFORMTYPE_UVEC3ARRAY, - UNIFORMTYPE_IVEC4ARRAY, UNIFORMTYPE_BVEC4ARRAY, UNIFORMTYPE_UVEC4ARRAY, UNIFORMTYPE_TEXTURE2D_ARRAY, + UNIFORMTYPE_IVEC4ARRAY, UNIFORMTYPE_BVEC4ARRAY, UNIFORMTYPE_UVEC4ARRAY, DEVICETYPE_WEBGL1, semanticToLocation, + UNIFORMTYPE_TEXTURE2D_ARRAY, PRIMITIVE_TRISTRIP, DEVICETYPE_WEBGL2, - DEVICETYPE_WEBGL1, UNIFORMTYPE_MAT4ARRAY } from '../constants.js'; From d69604108c9bacca66bd1255f82b911446d8ec1b Mon Sep 17 00:00:00 2001 From: Liam Don Date: Fri, 12 Jan 2024 11:55:54 -0800 Subject: [PATCH 19/43] Minimize import diff --- src/platform/graphics/webgl/webgl-graphics-device.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/graphics/webgl/webgl-graphics-device.js b/src/platform/graphics/webgl/webgl-graphics-device.js index 69c299580e0..d759167a591 100644 --- a/src/platform/graphics/webgl/webgl-graphics-device.js +++ b/src/platform/graphics/webgl/webgl-graphics-device.js @@ -22,12 +22,12 @@ import { UNIFORMTYPE_ITEXTURECUBE, UNIFORMTYPE_UTEXTURECUBE, UNIFORMTYPE_ITEXTURE3D, UNIFORMTYPE_UTEXTURE3D, UNIFORMTYPE_ITEXTURE2D_ARRAY, UNIFORMTYPE_UTEXTURE2D_ARRAY, UNIFORMTYPE_INTARRAY, UNIFORMTYPE_UINTARRAY, UNIFORMTYPE_BOOLARRAY, UNIFORMTYPE_IVEC2ARRAY, UNIFORMTYPE_BVEC2ARRAY, UNIFORMTYPE_UVEC2ARRAY, UNIFORMTYPE_IVEC3ARRAY, UNIFORMTYPE_BVEC3ARRAY, UNIFORMTYPE_UVEC3ARRAY, - UNIFORMTYPE_IVEC4ARRAY, UNIFORMTYPE_BVEC4ARRAY, UNIFORMTYPE_UVEC4ARRAY, DEVICETYPE_WEBGL1, + UNIFORMTYPE_IVEC4ARRAY, UNIFORMTYPE_BVEC4ARRAY, UNIFORMTYPE_UVEC4ARRAY, UNIFORMTYPE_MAT4ARRAY, semanticToLocation, UNIFORMTYPE_TEXTURE2D_ARRAY, PRIMITIVE_TRISTRIP, DEVICETYPE_WEBGL2, - UNIFORMTYPE_MAT4ARRAY + DEVICETYPE_WEBGL1 } from '../constants.js'; import { GraphicsDevice } from '../graphics-device.js'; From ad242c9cb8a067eb7a6c444481660ef8098bab1c Mon Sep 17 00:00:00 2001 From: Liam Don Date: Sun, 14 Jan 2024 12:42:04 -0800 Subject: [PATCH 20/43] Sand simulation example --- .../scripts/generate-standalone-files.mjs | 6 +- examples/src/examples/graphics/index.mjs | 1 + .../examples/graphics/integer-textures.mjs | 574 ++++++++++++++++++ examples/src/options.mjs | 2 +- src/platform/graphics/bind-group-format.js | 19 +- src/platform/graphics/constants.js | 2 + .../graphics/shader-chunks/frag/gles2.js | 2 + .../graphics/shader-chunks/frag/gles3.js | 23 +- .../graphics/shader-chunks/frag/webgpu.js | 22 +- .../graphics/shader-chunks/vert/gles3.js | 2 + .../graphics/shader-chunks/vert/webgpu.js | 3 + src/platform/graphics/shader-processor.js | 18 +- src/platform/graphics/shader-utils.js | 14 +- .../webgpu/webgpu-bind-group-format.js | 6 +- src/platform/graphics/webgpu/webgpu-shader.js | 2 + .../graphics/webgpu/webgpu-texture.js | 6 +- src/scene/graphics/render-pass-shader-quad.js | 4 +- src/scene/shader-lib/utils.js | 12 +- 18 files changed, 678 insertions(+), 40 deletions(-) create mode 100644 examples/src/examples/graphics/integer-textures.mjs diff --git a/examples/scripts/generate-standalone-files.mjs b/examples/scripts/generate-standalone-files.mjs index 34d09ca291f..d84c55eb5f5 100644 --- a/examples/scripts/generate-standalone-files.mjs +++ b/examples/scripts/generate-standalone-files.mjs @@ -136,6 +136,10 @@ ${exampleClass.example.toString()} console.warn('Picked WebGPU but example is not supported on WebGPU, defaulting to WebGL2'); return 'webgl2'; } + if (last === 'webgl1' && ${exampleClass.WEBGL1_DISABLED === true}) { + console.warn('Picked WebGL1 but example is not supported on WebGL1, defaulting to WebGL2'); + return 'webgl2'; + } return last; } else if (${Boolean(exampleClass.WEBGPU_ENABLED)}) { let preferredDevice = 'webgpu'; @@ -144,7 +148,7 @@ ${exampleClass.example.toString()} preferredDevice = 'webgl2'; } return window.top.preferredGraphicsDevice || preferredDevice; - } else if (['webgl1', 'webgl2'].includes(window.top.preferredGraphicsDevice)) { + } else if (['webgl1', 'webgl2'].includes(window.top.preferredGraphicsDevice) && !${Boolean(exampleClass.WEBGL1_DISABLED)}) { return window.top.preferredGraphicsDevice; } else { return 'webgl2'; diff --git a/examples/src/examples/graphics/index.mjs b/examples/src/examples/graphics/index.mjs index 3aa8af4c44d..21c5449b439 100644 --- a/examples/src/examples/graphics/index.mjs +++ b/examples/src/examples/graphics/index.mjs @@ -14,6 +14,7 @@ export * from "./grab-pass.mjs"; export * from "./ground-fog.mjs"; export * from "./hardware-instancing.mjs"; export * from "./hierarchy.mjs"; +export * from "./integer-textures.mjs"; export * from "./layers.mjs"; export * from "./lights-baked-a-o.mjs"; export * from "./lights-baked.mjs"; diff --git a/examples/src/examples/graphics/integer-textures.mjs b/examples/src/examples/graphics/integer-textures.mjs new file mode 100644 index 00000000000..2055fc3f12e --- /dev/null +++ b/examples/src/examples/graphics/integer-textures.mjs @@ -0,0 +1,574 @@ +import * as pc from 'playcanvas'; + +/** + * @param {import('../../app/example.mjs').ControlOptions} options - The options. + * @returns {JSX.Element} The returned JSX Element. + */ +function controls({ observer, ReactPCUI, jsx, fragment }) { + const { BindingTwoWay, Container, Button, LabelGroup, Panel, SliderInput, SelectInput } = ReactPCUI; + return fragment( + jsx(Panel, { headerText: 'Sand simulation' }, + jsx(LabelGroup, { text: 'Brush' }, + jsx(SelectInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'options.brush' }, + type: "string", + value: 1, + options: [ + { v: 1, t: 'Sand' }, + { v: 2, t: 'Stone' }, + { v: 3, t: 'Water' } + ] + }) + ), + jsx(LabelGroup, { text: 'Brush size' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'options.brushSize' }, + value: 3, + min: 1, + max: 12, + precision: 0 + }) + ), + jsx(Container, { flex: true, flexGrow: 1 }, + jsx(Button, { + text: 'Reset', + onClick: () => observer.emit('reset') + }) + ) + ) + ); +} + +/** + * @typedef {{ 'sandSimulation.frag': string, 'renderOutput.frag': string }} Files + * @typedef {import('../../options.mjs').ExampleOptions} Options + * @param {Options} options - The example options. + * @returns {Promise} The example application. + */ +async function example({ canvas, data, deviceType, assetPath, files, glslangPath, twgslPath, dracoPath }) { + + const STEPS_PER_FRAME = 4; + + const PLANE_WIDTH = 10; + const PLANE_HEIGHT = 10; + + const TEXTURE_RATIO = PLANE_WIDTH / PLANE_HEIGHT; + const TEXTURE_HEIGHT = 512; + const TEXTURE_WIDTH = TEXTURE_HEIGHT * TEXTURE_RATIO; + + // set up and load draco module, as the glb we load is draco compressed + pc.WasmModule.setConfig('DracoDecoderModule', { + glueUrl: dracoPath + 'draco.wasm.js', + wasmUrl: dracoPath + 'draco.wasm.wasm', + fallbackUrl: dracoPath + 'draco.js' + }); + + const assets = { + noiseTexture: new pc.Asset('noise', 'texture', { url: assetPath + 'textures/clouds.jpg' }), + font: new pc.Asset('font', 'font', { url: assetPath + 'fonts/courier.json' }) + }; + + const gfxOptions = { + deviceTypes: [deviceType], + glslangUrl: glslangPath + 'glslang.js', + twgslUrl: twgslPath + 'twgsl.js' + }; + + const device = await pc.createGraphicsDevice(canvas, gfxOptions); + const createOptions = new pc.AppOptions(); + createOptions.graphicsDevice = device; + createOptions.keyboard = new pc.Keyboard(document.body); + + createOptions.componentSystems = [ + pc.RenderComponentSystem, + pc.CameraComponentSystem, + pc.LightComponentSystem, + pc.ScreenComponentSystem, + pc.ElementComponentSystem + ]; + createOptions.resourceHandlers = [ + // @ts-ignore + pc.TextureHandler, + // @ts-ignore + pc.FontHandler + ]; + + const app = new pc.AppBase(canvas); + app.init(createOptions); + + // Set the canvas to fill the window and automatically change resolution to be the same as the canvas size + app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW); + app.setCanvasResolution(pc.RESOLUTION_AUTO); + + // Ensure canvas is resized when window changes size + const resize = () => app.resizeCanvas(); + window.addEventListener('resize', resize); + app.on('destroy', () => { + window.removeEventListener('resize', resize); + }); + + // Helpers to create integer pixel buffers and render targets which we will ping-pong between + const createPixelColorBuffer = (i) => { + return new pc.Texture(device, { + name: `PixelBuffer_${i}`, + width: TEXTURE_WIDTH, + height: TEXTURE_HEIGHT, + format: pc.PIXELFORMAT_RG8UI, + addressU: pc.ADDRESS_CLAMP_TO_EDGE, + addressV: pc.ADDRESS_CLAMP_TO_EDGE + }); + }; + const createPixelRenderTarget = (i, colorBuffer) => { + return new pc.RenderTarget({ + name: `PixelRenderTarget_${i}`, + colorBuffer: colorBuffer + }); + }; + + const sandShader = pc.createShaderFromCode( + device, + files['quad.vert'], + files['sandSimulation.frag'], + 'SandShader', + { aPosition: pc.SEMANTIC_POSITION }, + false, + 'uvec2' + ); + + const outputShader = pc.createShaderFromCode( + device, + files['quad.vert'], + files['renderOutput.frag'], + 'RenderOutputShader', + { aPosition: pc.SEMANTIC_POSITION } + ); + + // Create our integer pixel buffers and render targets + const pixelColorBuffers = [createPixelColorBuffer(0), createPixelColorBuffer(1)]; + const pixelRenderTargets = [ + createPixelRenderTarget(0, pixelColorBuffers[0]), + createPixelRenderTarget(1, pixelColorBuffers[1]) + ]; + + const sourceTexture = pixelColorBuffers[0]; + const sourceRenderTarget = pixelRenderTargets[0]; + const sandRenderTarget = pixelRenderTargets[1]; + const sandTexture = pixelColorBuffers[1]; + + const sourceTextureData = sourceTexture.lock(); + for (let x = 0; x < sourceTexture.width; x++) { + for (let y = 0; y < sourceTexture.height; y++) { + const i = (y * sourceTexture.width + x) * 2; + if (x > sourceTexture.width * 0.3 && x < sourceTexture.width * 0.7 && y > sourceTexture.height * 0.7 && y < sourceTexture.height * 0.8) { + sourceTextureData[i] = 2; + } else if (Math.random() > 0.94) { + sourceTextureData[i] = 1; + sourceTextureData[i] |= (Math.floor(Math.random() * 15) << 4); + } else { + sourceTextureData[i] = 0; + } + } + } + sourceTexture.unlock(); + + const outputTexture = new pc.Texture(device, { + name: 'OutputTexture', + width: TEXTURE_WIDTH, + height: TEXTURE_HEIGHT, + format: pc.PIXELFORMAT_RGBA8, + minFilter: pc.FILTER_LINEAR_MIPMAP_LINEAR, + magFilter: pc.FILTER_LINEAR, + addressU: pc.ADDRESS_REPEAT, + addressV: pc.ADDRESS_REPEAT + }); + + const outputRenderTarget = createPixelRenderTarget(2, outputTexture); + + + const assetListLoader = new pc.AssetListLoader(Object.values(assets), app.assets); + assetListLoader.load(() => { + + data.set('options', { + brush: 1, + brushSize: 3 + }); + + app.start(); + + // Create an Entity with a camera component + const cameraEntity = new pc.Entity(); + cameraEntity.addComponent("camera", { + clearColor: new pc.Color(0.4, 0.45, 0.5), + farClip: 500 + }); + + // add camera to the world + cameraEntity.setPosition(0, 5, 15); + cameraEntity.lookAt(0, 5, 0); + app.root.addChild(cameraEntity); + if (cameraEntity.camera === undefined) throw new Error('Camera component expected'); + + // create material used on the geometry + const groundMaterial = new pc.StandardMaterial(); + groundMaterial.gloss = 0.6; + groundMaterial.metalness = 0.4; + groundMaterial.diffuse = new pc.Color(0.95, 0.85, 0.85); + groundMaterial.useMetalness = true; + groundMaterial.useLighting = true; + groundMaterial.update(); + + const ground = new pc.Entity(); + ground.addComponent('render', { + castShadows: false, + castShadowsLightmap: false, + lightmapped: false, + type: "plane", + material: groundMaterial + }); + app.root.addChild(ground); + ground.setLocalPosition(0, 0, 0); + ground.setLocalScale(40, 40, 40); + + // Create a directional light + const lightEntity = new pc.Entity(); + lightEntity.addComponent("light", { + type: "directional", + color: pc.Color.WHITE, + range: 100, + intensity: 1, + shadowDistance: 256, + castShadows: true, + shadowBias: 0.1 + // normalOffsetBias: 0.2 + }); + lightEntity.setLocalEulerAngles(60, 40, 0); + lightEntity.setLocalPosition(0, 10, 0); + app.root.addChild(lightEntity); + + // create a plane called gameScreen to display rendered texture + const gameScreen = new pc.Entity(); + gameScreen.addComponent('render', { + castShadows: true, + receiveShadows: false, + castShadowsLightmap: false, + lightmapped: false, + type: "plane" + }); + if (!gameScreen.render) throw new Error('Render component expected'); + gameScreen.setLocalPosition(0, 5, 0); + gameScreen.setLocalScale(PLANE_WIDTH, 1, PLANE_HEIGHT); + gameScreen.setEulerAngles(90, 0, 0); + const gamePlane = new pc.Plane(new pc.Vec3(0, 0, 1), 0); + // const gameScreenAabb = new pc.BoundingBox(); + // gameScreenAabb.center = new pc.Vec3(0, 5, 1); + // gameScreenAabb.halfExtents = new pc.Vec3(7.5, 5, 1); + + /** @type {pc.StandardMaterial} */ + const gameScreenMaterial = gameScreen.render.material; + gameScreenMaterial.emissiveMap = outputTexture; // assign the rendered texture as an emissive texture + gameScreenMaterial.useLighting = false; + gameScreenMaterial.update(); + + app.root.addChild(gameScreen); + + // Slightly rotate the camera to face the mouse cursor + const mouse = new pc.Mouse(document.body); + const lookRange = 1.5; + const mouseRay = new pc.Ray(); + const planePoint = new pc.Vec3(); + const mousePos = new pc.Vec2(); + let mouseState = 0; + mouse.disableContextMenu(); + mouse.on(pc.EVENT_MOUSEDOWN, function (event) { + if (event.button === pc.MOUSEBUTTON_LEFT) { + mouseState = 1; + } else if (event.button === pc.MOUSEBUTTON_RIGHT) { + mouseState = 2; + } + }); + mouse.on(pc.EVENT_MOUSEUP, function () { + mouseState = 0; + }); + mouse.on(pc.EVENT_MOUSEMOVE, function (event) { + const x = event.x; + const y = event.y; + + mousePos.x = x; + mousePos.y = y; + + const centerX = app.graphicsDevice.width / 2; + const centerY = app.graphicsDevice.height / 2; + + const xOffset = (x - centerX) / app.graphicsDevice.width; + const yOffset = (y - centerY) / app.graphicsDevice.height; + + cameraEntity.lookAt(xOffset * lookRange, 5 - yOffset * lookRange, 0); + if (cameraEntity.camera) { + cameraEntity.camera.screenToWorld(event.x, event.y, cameraEntity.camera.farClip, mouseRay.direction); + mouseRay.origin.copy(cameraEntity.getPosition()); + mouseRay.direction.sub(mouseRay.origin).normalize(); + gamePlane.intersectsRay(mouseRay, planePoint); + planePoint.x = (PLANE_WIDTH / 2) + planePoint.x; + planePoint.y = PLANE_HEIGHT - planePoint.y; + mousePos.set(planePoint.x / PLANE_WIDTH, planePoint.y / PLANE_HEIGHT); + } + }); + + const worldLayer = app.scene.layers.getLayerByName("World"); + if (!worldLayer) throw new Error('World layer expected'); + + const mousePosition = new Float32Array(2); + + // Create a 2D screen + const screen = new pc.Entity(); + screen.addComponent("screen", { + referenceResolution: new pc.Vec2(1280, 720), + scaleBlend: 0.5, + scaleMode: pc.SCALEMODE_BLEND, + screenSpace: true + }); + app.root.addChild(screen); + + // Markup Text with wrap + const textMarkup = new pc.Entity(); + textMarkup.setLocalPosition(0, 50, 0); + textMarkup.addComponent("element", { + alignment: new pc.Vec2(1.0, 0.0), + pivot: new pc.Vec2(0.0, 0.0), + anchor: new pc.Vec4(0.25, 0.01, 0.96, 0.99), + fontAsset: assets.font.id, + fontSize: 16, + text: 'Left click: add\nRight click: remove\nPress space: reset', + width: 500, + height: 100, + autoWidth: false, + autoHeight: false, + wrapLines: true, + enableMarkup: true, + type: pc.ELEMENTTYPE_TEXT + }); + screen.addChild(textMarkup); + + // update things every frame + let passNum = 0; + app.on("update", function (/** @type {number} */dt) { + + mousePosition[0] = mousePos.x; + mousePosition[1] = mousePos.y; + + const brushRadius = data.get('options.brushSize') / Math.max(TEXTURE_WIDTH, TEXTURE_HEIGHT); + const brush = data.get('options.brush') ?? 1; + + // Run the sand simulation shader + for (let i = 0; i < STEPS_PER_FRAME; i++) { + device.scope.resolve('sourceTexture').setValue(sourceTexture); + device.scope.resolve('mousePosition').setValue(mousePosition); + device.scope.resolve('mouseButton').setValue(mouseState); + device.scope.resolve('brush').setValue(brush); + device.scope.resolve('brushRadius').setValue(brushRadius); + device.scope.resolve('passNum').setValue(passNum); + pc.drawQuadWithShader(device, sandRenderTarget, sandShader); + device.copyRenderTarget(sandRenderTarget, sourceRenderTarget, true, false); + passNum = (passNum + 1) % 3; + } + + device.scope.resolve('sourceTexture').setValue(sandRenderTarget.colorBuffer); + device.scope.resolve('mousePosition').setValue(mousePosition); + device.scope.resolve('brushRadius').setValue(brushRadius); + pc.drawQuadWithShader(device, outputRenderTarget, outputShader); + + }); + }); + return app; +} + +export class IntegerTextureExample { + static CATEGORY = 'Graphics'; + static WEBGPU_ENABLED = true; + static WEBGL1_DISABLED = true; + static example = example; + static controls = controls; + static sharedShaderChunks = { + 'sandCommon.frag': /* glsl */` + const uint AIR = 0u; + const uint SAND = 1u; + const uint WALL = 2u; + const uint WATER = 3u; + + bool isInBounds(ivec2 c, ivec2 size) { + return c.x > 0 && c.x < size.x - 1 && c.y > 0 && c.y < size.y - 1; + } + + int seed; + + float rand() { + int n = (seed++ << 13) ^ seed; + return float((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 2147483647.0; + } + + #define INIT_SEED() \ + seed = int(uv0.x + uv0.y + float(coord.x) + float(coord.y)); \ + seed = int(rand() * 2147483647.0) + int(passNum); + + struct Particle { + uint element; // 3 bits + bool movedThisFrame; // 1 bit + uint shade; // 4 bits + }; + + uvec2 pack(Particle particle) { + uint packed = 0u; + packed |= (particle.element & 0x7u); // Store element in the lowest 3 bits + packed |= ((particle.movedThisFrame ? 1u : 0u) << 3); // Store movedThisFrame in the next bit + packed |= (particle.shade << 4); // Store shade in the next 4 bits + + return uvec2(packed, 0u); // Second component is reserved/unused + } + + Particle unpack(uvec2 pixel) { + uint packed = pixel.x; + + Particle particle; + particle.element = packed & 0x7u; // Extract lowest 3 bits + particle.movedThisFrame = ((packed >> 3) & 0x1u) != 0u; // Extract the next bit + particle.shade = (packed >> 4) & 0xFu; // Extract the next 4 bits + + return particle; + } + + Particle getParticle(ivec2 c) { + uvec2 val = texelFetch(sourceTexture, c, 0).rg; + return unpack(val); + } + ` + }; + + static FILES = { + 'quad.vert': /* glsl */` + attribute vec2 aPosition; + varying vec2 uv0; + void main(void) + { + gl_Position = vec4(aPosition, 0.0, 1.0); + uv0 = getImageEffectUV((aPosition.xy + 1.0) * 0.5); + } + `, + 'sandSimulation.frag': /* glsl */` + precision highp usampler2D; + + uniform usampler2D sourceTexture; + uniform vec2 mousePosition; + uniform uint mouseButton; + uniform uint passNum; + uniform uint brush; + uniform float brushRadius; + + varying vec2 uv0; + + ${IntegerTextureExample.sharedShaderChunks['sandCommon.frag']} + + void main() { + + ivec2 size = textureSize(sourceTexture, 0); + ivec2 coord = ivec2(uv0 * vec2(size)); + + if (!isInBounds(coord, size)) { + //gl_FragColor = uvec2(WALL, 0u); + gl_FragColor = uvec2(WALL, 0u); + return; + } + + Particle currentParticle = getParticle(coord); + + Particle nextState = currentParticle; + nextState.movedThisFrame = false; + float mouseDist = distance(mousePosition, uv0); + + INIT_SEED(); + + int dir = int(passNum % 3u) - 1; + if (rand() > 0.75) { + dir = ((dir + 1) % 3) - 1; + } + + int upDown = 1; + if (rand() > 0.99) { + upDown = 0; + } + + if (mouseButton == 1u && mouseDist < brushRadius) { + nextState.element = brush; + nextState.movedThisFrame = true; + nextState.shade = uint(rand() * 15.0); + } else if (mouseButton == 2u && mouseDist < brushRadius) { + nextState.element = AIR; + nextState.movedThisFrame = false; + nextState.shade = uint(rand() * 15.0); + } else if (currentParticle.element == AIR) { + Particle particleAbove = getParticle(coord + ivec2(dir, -upDown)); + if (particleAbove.element == SAND) { + nextState = particleAbove; + nextState.movedThisFrame = true; + } + } else if (currentParticle.element == SAND) { + Particle particleBelow = getParticle(coord + ivec2(-dir, upDown)); + if ((particleBelow.element == AIR) && !particleBelow.movedThisFrame) { + nextState = particleBelow; + nextState.movedThisFrame = false; + } + } + + gl_FragColor = pack(nextState); + } + `, + 'renderOutput.frag': /* glsl */` + precision highp usampler2D; + uniform usampler2D sourceTexture; + uniform vec2 mousePosition; + uniform float brushRadius; + varying vec2 uv0; + + vec3 whiteColor = vec3(1.0); + vec3 skyBlueColor = vec3(0.6, 0.7, 0.8); + vec3 yellowSandColor = vec3(0.73, 0.58, 0.26); + vec3 grayWallColor = vec3(0.5, 0.5, 0.5); + vec3 waterBlueColor = vec3(0.2, 0.3, 0.8); + + float circle( vec2 p, float r ) { + return length(p) - r; + } + + const float circleOutline = 0.01; + + ${IntegerTextureExample.sharedShaderChunks['sandCommon.frag']} + + void main() { + ivec2 size = textureSize(sourceTexture, 0); + ivec2 coord = ivec2(uv0 * vec2(size)); + Particle particle = getParticle(coord); + + vec3 gameColor = skyBlueColor; + if (particle.element == SAND) { + gameColor = mix(yellowSandColor, whiteColor, (float(particle.shade) / 15.0) * 0.5); + } else if (particle.element == WALL) { + gameColor = grayWallColor; + } else if (particle.element == WATER) { + gameColor = mix(waterBlueColor, whiteColor, (float(particle.shade) / 15.0) * 0.75); + } + + float d = length(uv0 - mousePosition); + float wd = fwidth(d); + float circle = smoothstep(brushRadius + wd, brushRadius, d); + float circleInner = smoothstep(brushRadius - circleOutline + wd, brushRadius - circleOutline, d); + float brush = max(circle - circleInner, 0.0) * 0.5; + + vec3 outColor = mix(gameColor, vec3(1.0), brush); + //vec3 outColor = mix(vec3(1.0), gameColor, circle); + + gl_FragColor = vec4(outColor, 1.0); + } + ` + }; +} diff --git a/examples/src/options.mjs b/examples/src/options.mjs index 942e8741c35..93c736c5e38 100644 --- a/examples/src/options.mjs +++ b/examples/src/options.mjs @@ -11,6 +11,6 @@ * @property {string} ammoPath - The ammo path. * @property {string} basisPath - The basis path. * @property {any} pcx - The pcx. - * @property {FILES} files - The files. * @template {Record} [FILES=Record] + * @property {FILES} files - The files. */ diff --git a/src/platform/graphics/bind-group-format.js b/src/platform/graphics/bind-group-format.js index 08abaa36449..fd0a6316527 100644 --- a/src/platform/graphics/bind-group-format.js +++ b/src/platform/graphics/bind-group-format.js @@ -3,7 +3,7 @@ import { Debug, DebugHelper } from '../../core/debug.js'; import { TEXTUREDIMENSION_2D, TEXTUREDIMENSION_CUBE, TEXTUREDIMENSION_3D, TEXTUREDIMENSION_2D_ARRAY, - SAMPLETYPE_FLOAT, PIXELFORMAT_RGBA8 + SAMPLETYPE_FLOAT, PIXELFORMAT_RGBA8, SAMPLETYPE_INT, SAMPLETYPE_UINT } from './constants.js'; let id = 0; @@ -180,7 +180,7 @@ class BindGroupFormat { let bindIndex = this.bufferFormats.length; this.textureFormats.forEach((format) => { - const textureType = textureDimensionInfo[format.textureDimension]; + let textureType = textureDimensionInfo[format.textureDimension]; Debug.assert(textureType, "Unsupported texture type", format.textureDimension); // handle texture2DArray by renaming the texture object and defining a replacement macro @@ -191,9 +191,20 @@ class BindGroupFormat { extraCode = `#define ${format.name} sampler2DArray(${format.name}${namePostfix}, ${format.name}_sampler)\n`; } + if (format.sampleType === SAMPLETYPE_INT) { + textureType = `i${textureType}`; + // code += `layout(set = ${bindGroup}, binding = ${bindIndex++}) uniform ${textureType} ${format.name}${namePostfix};\n` + + // extraCode; + } else if (format.sampleType === SAMPLETYPE_UINT) { + textureType = `u${textureType}`; + // code += `layout(set = ${bindGroup}, binding = ${bindIndex++}) uniform ${textureType} ${format.name}${namePostfix};\n` + + // extraCode; + } + code += `layout(set = ${bindGroup}, binding = ${bindIndex++}) uniform ${textureType} ${format.name}${namePostfix};\n` + - `layout(set = ${bindGroup}, binding = ${bindIndex++}) uniform sampler ${format.name}_sampler;\n` + - extraCode; + `layout(set = ${bindGroup}, binding = ${bindIndex++}) uniform sampler ${format.name}_sampler;\n` + + extraCode; + }); return code; diff --git a/src/platform/graphics/constants.js b/src/platform/graphics/constants.js index 3ef1743e78f..45b1eb239b4 100644 --- a/src/platform/graphics/constants.js +++ b/src/platform/graphics/constants.js @@ -1240,6 +1240,8 @@ export const TEXTUREDIMENSION_3D = '3d'; export const SAMPLETYPE_FLOAT = 0; export const SAMPLETYPE_UNFILTERABLE_FLOAT = 1; export const SAMPLETYPE_DEPTH = 2; +export const SAMPLETYPE_INT = 3; +export const SAMPLETYPE_UINT = 4; /** * Texture data is not stored a specific projection format. diff --git a/src/platform/graphics/shader-chunks/frag/gles2.js b/src/platform/graphics/shader-chunks/frag/gles2.js index 62b11aa3bb5..3f887dc84eb 100644 --- a/src/platform/graphics/shader-chunks/frag/gles2.js +++ b/src/platform/graphics/shader-chunks/frag/gles2.js @@ -31,6 +31,8 @@ export default /* glsl */` #endif #define texture2DBias texture2D +#define itexture2D texture2D +#define utexture2D texture2D // pass / accept shadow map or texture as a function parameter, on webgl this is simply passed as is // but this is needed for WebGPU diff --git a/src/platform/graphics/shader-chunks/frag/gles3.js b/src/platform/graphics/shader-chunks/frag/gles3.js index 4e3c2716c4d..e63c7a064be 100644 --- a/src/platform/graphics/shader-chunks/frag/gles3.js +++ b/src/platform/graphics/shader-chunks/frag/gles3.js @@ -1,45 +1,50 @@ export default /* glsl */` -layout(location = 0) out highp vec4 pc_fragColor; + +#ifndef outType +#define outType vec4 +#endif + +layout(location = 0) out highp outType pc_fragColor; #ifndef REMOVE_COLOR_ATTACHMENT_1 #if COLOR_ATTACHMENT_1 -layout(location = 1) out highp vec4 pc_fragColor1; +layout(location = 1) out highp outType pc_fragColor1; #endif #endif #ifndef REMOVE_COLOR_ATTACHMENT_2 #if COLOR_ATTACHMENT_2 -layout(location = 2) out highp vec4 pc_fragColor2; +layout(location = 2) out highp outType pc_fragColor2; #endif #endif #ifndef REMOVE_COLOR_ATTACHMENT_3 #if COLOR_ATTACHMENT_3 -layout(location = 3) out highp vec4 pc_fragColor3; +layout(location = 3) out highp outType pc_fragColor3; #endif #endif #ifndef REMOVE_COLOR_ATTACHMENT_4 #if COLOR_ATTACHMENT_4 -layout(location = 4) out highp vec4 pc_fragColor4; +layout(location = 4) out highp outType pc_fragColor4; #endif #endif #ifndef REMOVE_COLOR_ATTACHMENT_5 #if COLOR_ATTACHMENT_5 -layout(location = 5) out highp vec4 pc_fragColor5; +layout(location = 5) out highp outType pc_fragColor5; #endif #endif #ifndef REMOVE_COLOR_ATTACHMENT_6 #if COLOR_ATTACHMENT_6 -layout(location = 6) out highp vec4 pc_fragColor6; +layout(location = 6) out highp outType pc_fragColor6; #endif #endif #ifndef REMOVE_COLOR_ATTACHMENT_7 #if COLOR_ATTACHMENT_7 -layout(location = 7) out highp vec4 pc_fragColor7; +layout(location = 7) out highp outType pc_fragColor7; #endif #endif @@ -66,6 +71,8 @@ layout(location = 7) out highp vec4 pc_fragColor7; #define texture2DGradEXT textureGrad #define texture2DProjGradEXT textureProjGrad #define textureCubeGradEXT textureGrad +#define utexture2D texture +#define itexture2D texture // sample shadows using textureGrad to remove derivatives in the dynamic loops (which are used by // clustered lighting) - as DirectX shader compiler tries to unroll the loops and takes long time diff --git a/src/platform/graphics/shader-chunks/frag/webgpu.js b/src/platform/graphics/shader-chunks/frag/webgpu.js index 35800ef77ff..6af67b2e2f9 100644 --- a/src/platform/graphics/shader-chunks/frag/webgpu.js +++ b/src/platform/graphics/shader-chunks/frag/webgpu.js @@ -3,14 +3,18 @@ export default /* glsl */` // texelFetch support and others #extension GL_EXT_samplerless_texture_functions : require -layout(location = 0) out highp vec4 pc_fragColor; -layout(location = 1) out highp vec4 pc_fragColor1; -layout(location = 2) out highp vec4 pc_fragColor2; -layout(location = 3) out highp vec4 pc_fragColor3; -layout(location = 4) out highp vec4 pc_fragColor4; -layout(location = 5) out highp vec4 pc_fragColor5; -layout(location = 6) out highp vec4 pc_fragColor6; -layout(location = 7) out highp vec4 pc_fragColor7; +#ifndef outType +#define outType vec4 +#endif + +layout(location = 0) out highp outType pc_fragColor; +layout(location = 1) out highp outType pc_fragColor1; +layout(location = 2) out highp outType pc_fragColor2; +layout(location = 3) out highp outType pc_fragColor3; +layout(location = 4) out highp outType pc_fragColor4; +layout(location = 5) out highp outType pc_fragColor5; +layout(location = 6) out highp outType pc_fragColor6; +layout(location = 7) out highp outType pc_fragColor7; #define gl_FragColor pc_fragColor @@ -29,6 +33,8 @@ layout(location = 7) out highp vec4 pc_fragColor7; #define textureCube(res, uv) texture(samplerCube(res, res ## _sampler), uv) #define textureCubeLodEXT(res, uv, lod) textureLod(samplerCube(res, res ## _sampler), uv, lod) #define textureShadow(res, uv) textureLod(sampler2DShadow(res, res ## _sampler), uv, 0.0) +#define itexture2D(res, uv) texture(isampler2D(res, res ## _sampler), uv) +#define utexture2D(res, uv) texture(usampler2D(res, res ## _sampler), uv) // TODO: implement other texture sampling macros // #define texture2DProj textureProj diff --git a/src/platform/graphics/shader-chunks/vert/gles3.js b/src/platform/graphics/shader-chunks/vert/gles3.js index d25c3def862..f5bc503d850 100644 --- a/src/platform/graphics/shader-chunks/vert/gles3.js +++ b/src/platform/graphics/shader-chunks/vert/gles3.js @@ -2,6 +2,8 @@ export default /* glsl */` #define attribute in #define varying out #define texture2D texture +#define utexture2D texture +#define itexture2D texture #define GL2 #define VERTEXSHADER `; diff --git a/src/platform/graphics/shader-chunks/vert/webgpu.js b/src/platform/graphics/shader-chunks/vert/webgpu.js index e9dd9c4c726..59150c6f19e 100644 --- a/src/platform/graphics/shader-chunks/vert/webgpu.js +++ b/src/platform/graphics/shader-chunks/vert/webgpu.js @@ -4,6 +4,9 @@ export default /* glsl */` #extension GL_EXT_samplerless_texture_functions : require #define texture2D(res, uv) texture(sampler2D(res, res ## _sampler), uv) +#define itexture2D(res, uv) texture(isampler2D(res, res ## _sampler), uv) +#define utexture2D(res, uv) texture(usampler2D(res, res ## _sampler), uv) +#define texelFetch(res, uv) textureLoad(res, vec2i(uv), 0) #define GL2 #define WEBGPU diff --git a/src/platform/graphics/shader-processor.js b/src/platform/graphics/shader-processor.js index 49c0225b774..0379044c827 100644 --- a/src/platform/graphics/shader-processor.js +++ b/src/platform/graphics/shader-processor.js @@ -5,7 +5,7 @@ import { UNIFORM_BUFFER_DEFAULT_SLOT_NAME, SAMPLETYPE_FLOAT, SAMPLETYPE_DEPTH, SAMPLETYPE_UNFILTERABLE_FLOAT, TEXTUREDIMENSION_2D, TEXTUREDIMENSION_2D_ARRAY, TEXTUREDIMENSION_CUBE, TEXTUREDIMENSION_3D, - TYPE_FLOAT32, TYPE_INT8, TYPE_INT16, TYPE_INT32, TYPE_FLOAT16 + TYPE_FLOAT32, TYPE_INT8, TYPE_INT16, TYPE_INT32, TYPE_FLOAT16, SAMPLETYPE_INT, SAMPLETYPE_UINT } from './constants.js'; import { UniformFormat, UniformBufferFormat } from './uniform-buffer-format.js'; import { BindGroupFormat, BindBufferFormat, BindTextureFormat } from './bind-group-format.js'; @@ -87,6 +87,8 @@ class UniformLine { } this.isSampler = this.type.indexOf('sampler') !== -1; + this.isSignedInt = this.type.indexOf('isampler') !== -1; + this.isUnsignedInt = this.type.indexOf('usampler') !== -1; } } @@ -284,10 +286,16 @@ class ShaderProcessor { // WebGpu does not currently support filtered float format textures, and so we map them to unfilterable type // as we sample them without filtering anyways let sampleType = SAMPLETYPE_FLOAT; - if (uniform.precision === 'highp') - sampleType = SAMPLETYPE_UNFILTERABLE_FLOAT; - if (shadowSamplers.has(uniform.type)) - sampleType = SAMPLETYPE_DEPTH; + if (uniform.isSignedInt) { + sampleType = SAMPLETYPE_INT; + } else if (uniform.isUnsignedInt) { + sampleType = SAMPLETYPE_UINT; + } else { + if (uniform.precision === 'highp') + sampleType = SAMPLETYPE_UNFILTERABLE_FLOAT; + if (shadowSamplers.has(uniform.type)) + sampleType = SAMPLETYPE_DEPTH; + } // dimension const dimension = textureDimensions[uniform.type]; diff --git a/src/platform/graphics/shader-utils.js b/src/platform/graphics/shader-utils.js index fd4a91eb950..6e2075f8ec8 100644 --- a/src/platform/graphics/shader-utils.js +++ b/src/platform/graphics/shader-utils.js @@ -51,19 +51,27 @@ class ShaderUtils { * @param {string} [options.fragmentExtensions] - The fragment shader extensions code. * @param {string} [options.fragmentPreamble] - The preamble string for the fragment shader. * @param {boolean} [options.useTransformFeedback] - Whether to use transform feedback. Defaults + * @param {string} [options.fragmentOutputType] - Output fragment shader type. Defaults to vec4. * to false. * @returns {object} Returns the created shader definition. */ static createDefinition(device, options) { Debug.assert(options); - const getDefines = (gpu, gl2, gl1, isVertex) => { + const getDefines = (gpu, gl2, gl1, isVertex, options) => { const deviceIntro = device.isWebGPU ? gpu : (device.isWebGL2 ? gl2 : ShaderUtils.gl1Extensions(device, options) + gl1); // a define per supported color attachment, which strips out unsupported output definitions in the deviceIntro let attachmentsDefine = ''; + + // Define the fragment shader output type, vec4 by default + if (!isVertex) { + const outType = options.fragmentOutputType ?? 'vec4'; + attachmentsDefine += `#define outType ${outType}\n`; + } + for (let i = 0; i < device.maxColorAttachments; i++) { attachmentsDefine += `#define COLOR_ATTACHMENT_${i}\n`; } @@ -74,7 +82,7 @@ class ShaderUtils { const name = options.name ?? 'Untitled'; // vertex code - const vertDefines = options.vertexDefines || getDefines(webgpuVS, gles3VS, '', true); + const vertDefines = options.vertexDefines || getDefines(webgpuVS, gles3VS, '', true, options); const vertCode = ShaderUtils.versionCode(device) + vertDefines + sharedFS + @@ -82,7 +90,7 @@ class ShaderUtils { options.vertexCode; // fragment code - const fragDefines = options.fragmentDefines || getDefines(webgpuFS, gles3FS, gles2FS, false); + const fragDefines = options.fragmentDefines || getDefines(webgpuFS, gles3FS, gles2FS, false, options); const fragCode = (options.fragmentPreamble || '') + ShaderUtils.versionCode(device) + fragDefines + diff --git a/src/platform/graphics/webgpu/webgpu-bind-group-format.js b/src/platform/graphics/webgpu/webgpu-bind-group-format.js index d741faab2c2..31b3b330abe 100644 --- a/src/platform/graphics/webgpu/webgpu-bind-group-format.js +++ b/src/platform/graphics/webgpu/webgpu-bind-group-format.js @@ -1,6 +1,6 @@ import { Debug, DebugHelper } from '../../../core/debug.js'; import { StringIds } from '../../../core/string-ids.js'; -import { SAMPLETYPE_FLOAT, SAMPLETYPE_UNFILTERABLE_FLOAT, SAMPLETYPE_DEPTH } from '../constants.js'; +import { SAMPLETYPE_FLOAT, SAMPLETYPE_UNFILTERABLE_FLOAT, SAMPLETYPE_DEPTH, SAMPLETYPE_INT, SAMPLETYPE_UINT } from '../constants.js'; import { WebgpuUtils } from './webgpu-utils.js'; import { gpuTextureFormats } from './constants.js'; @@ -9,11 +9,15 @@ const samplerTypes = []; samplerTypes[SAMPLETYPE_FLOAT] = 'filtering'; samplerTypes[SAMPLETYPE_UNFILTERABLE_FLOAT] = 'non-filtering'; samplerTypes[SAMPLETYPE_DEPTH] = 'comparison'; +samplerTypes[SAMPLETYPE_INT] = 'comparison'; +samplerTypes[SAMPLETYPE_UINT] = 'comparison'; const sampleTypes = []; sampleTypes[SAMPLETYPE_FLOAT] = 'float'; sampleTypes[SAMPLETYPE_UNFILTERABLE_FLOAT] = 'unfilterable-float'; sampleTypes[SAMPLETYPE_DEPTH] = 'depth'; +sampleTypes[SAMPLETYPE_INT] = 'sint'; +sampleTypes[SAMPLETYPE_UINT] = 'uint'; const stringIds = new StringIds(); diff --git a/src/platform/graphics/webgpu/webgpu-shader.js b/src/platform/graphics/webgpu/webgpu-shader.js index 565a1ce9172..3ad6ee191d6 100644 --- a/src/platform/graphics/webgpu/webgpu-shader.js +++ b/src/platform/graphics/webgpu/webgpu-shader.js @@ -126,6 +126,8 @@ class WebgpuShader { this.processed = processed; }); + console.log("Processing shader", shader); + this._vertexCode = this.transpile(processed.vshader, 'vertex', shader.definition.vshader); this._fragmentCode = this.transpile(processed.fshader, 'fragment', shader.definition.fshader); diff --git a/src/platform/graphics/webgpu/webgpu-texture.js b/src/platform/graphics/webgpu/webgpu-texture.js index e36dd4dde06..705e0fa02cb 100644 --- a/src/platform/graphics/webgpu/webgpu-texture.js +++ b/src/platform/graphics/webgpu/webgpu-texture.js @@ -8,7 +8,7 @@ import { PIXELFORMAT_RGBA16F, PIXELFORMAT_RGBA32F, PIXELFORMAT_DEPTHSTENCIL, SAMPLETYPE_UNFILTERABLE_FLOAT, SAMPLETYPE_DEPTH, FILTER_NEAREST, FILTER_LINEAR, FILTER_NEAREST_MIPMAP_NEAREST, FILTER_NEAREST_MIPMAP_LINEAR, - FILTER_LINEAR_MIPMAP_NEAREST, FILTER_LINEAR_MIPMAP_LINEAR, isIntegerPixelFormat + FILTER_LINEAR_MIPMAP_NEAREST, FILTER_LINEAR_MIPMAP_LINEAR, isIntegerPixelFormat, SAMPLETYPE_INT, SAMPLETYPE_UINT } from '../constants.js'; import { TextureUtils } from '../texture-utils.js'; import { WebgpuDebug } from './webgpu-debug.js'; @@ -215,7 +215,7 @@ class WebgpuTexture { sampleType = SAMPLETYPE_DEPTH; } - if (sampleType === SAMPLETYPE_DEPTH) { + if (sampleType === SAMPLETYPE_DEPTH || sampleType === SAMPLETYPE_INT || sampleType === SAMPLETYPE_UINT) { // depth compare sampling descr.compare = 'less'; @@ -225,7 +225,7 @@ class WebgpuTexture { } else if (sampleType === SAMPLETYPE_UNFILTERABLE_FLOAT) { - // webgpu cannot currently filter float / half float textures + // webgpu cannot currently filter float / half float textures, or integer textures descr.magFilter = 'nearest'; descr.minFilter = 'nearest'; descr.mipmapFilter = 'nearest'; diff --git a/src/scene/graphics/render-pass-shader-quad.js b/src/scene/graphics/render-pass-shader-quad.js index 74c26654cde..ce92c3102fc 100644 --- a/src/scene/graphics/render-pass-shader-quad.js +++ b/src/scene/graphics/render-pass-shader-quad.js @@ -89,10 +89,10 @@ class RenderPassShaderQuad extends RenderPass { return this._shader; } - createQuadShader(name, fs) { + createQuadShader(name, fs, fragmentOutputType = 'vec4') { return createShaderFromCode(this.device, RenderPassShaderQuad.quadVertexShader, fs, name, { aPosition: SEMANTIC_POSITION - }); + }, false, fragmentOutputType); } destroy() { diff --git a/src/scene/shader-lib/utils.js b/src/scene/shader-lib/utils.js index 8ed0eedc013..eaaed4ae081 100644 --- a/src/scene/shader-lib/utils.js +++ b/src/scene/shader-lib/utils.js @@ -13,14 +13,16 @@ import { ShaderGenerator } from './programs/shader-generator.js'; * @param {string} vsName - The vertex shader chunk name. * @param {string} fsName - The fragment shader chunk name. * @param {boolean} [useTransformFeedback] - Whether to use transform feedback. Defaults to false. + * @param {string} [fragmentOutputType] - Output fragment shader type. Defaults to vec4. * @returns {Shader} The newly created shader. */ -function createShader(device, vsName, fsName, useTransformFeedback = false) { +function createShader(device, vsName, fsName, useTransformFeedback = false, fragmentOutputType = 'vec4') { return new Shader(device, ShaderUtils.createDefinition(device, { name: `${vsName}_${fsName}`, vertexCode: shaderChunks[vsName], fragmentCode: shaderChunks[fsName], - useTransformFeedback: useTransformFeedback + useTransformFeedback: useTransformFeedback, + fragmentOutputType })); } @@ -40,9 +42,10 @@ function createShader(device, vsName, fsName, useTransformFeedback = false) { * attribute names to semantics SEMANTIC_*. This enables the engine to match vertex buffer data as * inputs to the shader. Defaults to undefined, which generates the default attributes. * @param {boolean} [useTransformFeedback] - Whether to use transform feedback. Defaults to false. + * @param {string} [fragmentOutputType] - Output fragment shader type. Defaults to vec4. * @returns {Shader} The newly created shader. */ -function createShaderFromCode(device, vsCode, fsCode, uniqueName, attributes, useTransformFeedback = false) { +function createShaderFromCode(device, vsCode, fsCode, uniqueName, attributes, useTransformFeedback = false, fragmentOutputType = 'vec4') { // the function signature has changed, fail if called incorrectly Debug.assert(typeof attributes !== 'boolean'); @@ -55,7 +58,8 @@ function createShaderFromCode(device, vsCode, fsCode, uniqueName, attributes, us vertexCode: vsCode, fragmentCode: fsCode, attributes: attributes, - useTransformFeedback: useTransformFeedback + useTransformFeedback: useTransformFeedback, + fragmentOutputType })); programLibrary.setCachedShader(uniqueName, shader); } From 1b98c07abf90eb82b3ac98c05e869f56bccecf76 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Sun, 14 Jan 2024 16:52:09 -0800 Subject: [PATCH 21/43] Clean up example a bit --- .../examples/graphics/integer-textures.mjs | 217 +++++++++--------- 1 file changed, 108 insertions(+), 109 deletions(-) diff --git a/examples/src/examples/graphics/integer-textures.mjs b/examples/src/examples/graphics/integer-textures.mjs index 2055fc3f12e..fb9caacb9d4 100644 --- a/examples/src/examples/graphics/integer-textures.mjs +++ b/examples/src/examples/graphics/integer-textures.mjs @@ -16,8 +16,9 @@ function controls({ observer, ReactPCUI, jsx, fragment }) { value: 1, options: [ { v: 1, t: 'Sand' }, - { v: 2, t: 'Stone' }, - { v: 3, t: 'Water' } + { v: 2, t: 'Orange Sand' }, + { v: 3, t: 'Gray Sand' }, + { v: 4, t: 'Stone' } ] }) ), @@ -25,9 +26,9 @@ function controls({ observer, ReactPCUI, jsx, fragment }) { jsx(SliderInput, { binding: new BindingTwoWay(), link: { observer, path: 'options.brushSize' }, - value: 3, + value: 8, min: 1, - max: 12, + max: 16, precision: 0 }) ), @@ -50,7 +51,6 @@ function controls({ observer, ReactPCUI, jsx, fragment }) { async function example({ canvas, data, deviceType, assetPath, files, glslangPath, twgslPath, dracoPath }) { const STEPS_PER_FRAME = 4; - const PLANE_WIDTH = 10; const PLANE_HEIGHT = 10; @@ -126,25 +126,6 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath colorBuffer: colorBuffer }); }; - - const sandShader = pc.createShaderFromCode( - device, - files['quad.vert'], - files['sandSimulation.frag'], - 'SandShader', - { aPosition: pc.SEMANTIC_POSITION }, - false, - 'uvec2' - ); - - const outputShader = pc.createShaderFromCode( - device, - files['quad.vert'], - files['renderOutput.frag'], - 'RenderOutputShader', - { aPosition: pc.SEMANTIC_POSITION } - ); - // Create our integer pixel buffers and render targets const pixelColorBuffers = [createPixelColorBuffer(0), createPixelColorBuffer(1)]; const pixelRenderTargets = [ @@ -155,24 +136,9 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath const sourceTexture = pixelColorBuffers[0]; const sourceRenderTarget = pixelRenderTargets[0]; const sandRenderTarget = pixelRenderTargets[1]; - const sandTexture = pixelColorBuffers[1]; - - const sourceTextureData = sourceTexture.lock(); - for (let x = 0; x < sourceTexture.width; x++) { - for (let y = 0; y < sourceTexture.height; y++) { - const i = (y * sourceTexture.width + x) * 2; - if (x > sourceTexture.width * 0.3 && x < sourceTexture.width * 0.7 && y > sourceTexture.height * 0.7 && y < sourceTexture.height * 0.8) { - sourceTextureData[i] = 2; - } else if (Math.random() > 0.94) { - sourceTextureData[i] = 1; - sourceTextureData[i] |= (Math.floor(Math.random() * 15) << 4); - } else { - sourceTextureData[i] = 0; - } - } - } - sourceTexture.unlock(); + // Create an output texture and render target to render + // a visual representation of the simulation const outputTexture = new pc.Texture(device, { name: 'OutputTexture', width: TEXTURE_WIDTH, @@ -183,16 +149,59 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath addressU: pc.ADDRESS_REPEAT, addressV: pc.ADDRESS_REPEAT }); - const outputRenderTarget = createPixelRenderTarget(2, outputTexture); + // This is shader runs the sand simulation + // It uses integer textures to store the state of each pixel + const sandShader = pc.createShaderFromCode( + device, + files['quad.vert'], + files['sandSimulation.frag'], + 'SandShader', + { aPosition: pc.SEMANTIC_POSITION }, + false, + 'uvec2' + ); + + // This shader reads the integer textures + // and renders a visual representation of the simulation + const outputShader = pc.createShaderFromCode( + device, + files['quad.vert'], + files['renderOutput.frag'], + 'RenderOutputShader', + { aPosition: pc.SEMANTIC_POSITION } + ); + + // Write the initial simulation state to the integer texture + const resetData = () => { + const sourceTextureData = sourceTexture.lock(); + for (let x = 0; x < sourceTexture.width; x++) { + for (let y = 0; y < sourceTexture.height; y++) { + const i = (y * sourceTexture.width + x) * 2; + if (x > sourceTexture.width * 0.3 && x < sourceTexture.width * 0.7 && y > sourceTexture.height * 0.7 && y < sourceTexture.height * 0.8) { + sourceTextureData[i] = 4; + sourceTextureData[i] |= (Math.floor(Math.random() * 15) << 4); + } else if (Math.random() > 0.94) { + sourceTextureData[i] = 1; + sourceTextureData[i] |= (Math.floor(Math.random() * 15) << 4); + } else { + sourceTextureData[i] = 0; + } + } + } + sourceTexture.unlock(); + }; + + resetData(); + data.on('reset', resetData); const assetListLoader = new pc.AssetListLoader(Object.values(assets), app.assets); assetListLoader.load(() => { data.set('options', { brush: 1, - brushSize: 3 + brushSize: 8 }); app.start(); @@ -210,7 +219,7 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath app.root.addChild(cameraEntity); if (cameraEntity.camera === undefined) throw new Error('Camera component expected'); - // create material used on the geometry + // create material used on the ground plane const groundMaterial = new pc.StandardMaterial(); groundMaterial.gloss = 0.6; groundMaterial.metalness = 0.4; @@ -219,6 +228,7 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath groundMaterial.useLighting = true; groundMaterial.update(); + // Create the ground plane const ground = new pc.Entity(); ground.addComponent('render', { castShadows: false, @@ -247,7 +257,8 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath lightEntity.setLocalPosition(0, 10, 0); app.root.addChild(lightEntity); - // create a plane called gameScreen to display rendered texture + // create a plane called gameScreen to display the sand + // simulation visualization texture const gameScreen = new pc.Entity(); gameScreen.addComponent('render', { castShadows: true, @@ -260,25 +271,24 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath gameScreen.setLocalPosition(0, 5, 0); gameScreen.setLocalScale(PLANE_WIDTH, 1, PLANE_HEIGHT); gameScreen.setEulerAngles(90, 0, 0); - const gamePlane = new pc.Plane(new pc.Vec3(0, 0, 1), 0); - // const gameScreenAabb = new pc.BoundingBox(); - // gameScreenAabb.center = new pc.Vec3(0, 5, 1); - // gameScreenAabb.halfExtents = new pc.Vec3(7.5, 5, 1); /** @type {pc.StandardMaterial} */ const gameScreenMaterial = gameScreen.render.material; - gameScreenMaterial.emissiveMap = outputTexture; // assign the rendered texture as an emissive texture + gameScreenMaterial.emissiveMap = outputTexture; gameScreenMaterial.useLighting = false; gameScreenMaterial.update(); - app.root.addChild(gameScreen); - // Slightly rotate the camera to face the mouse cursor + // Create a matching plane for mouse picking + const gamePlane = new pc.Plane(new pc.Vec3(0, 0, 1), 0); + + // Setup mouse controls const mouse = new pc.Mouse(document.body); const lookRange = 1.5; const mouseRay = new pc.Ray(); const planePoint = new pc.Vec3(); const mousePos = new pc.Vec2(); + const mouseUniform = new Float32Array(2); let mouseState = 0; mouse.disableContextMenu(); mouse.on(pc.EVENT_MOUSEDOWN, function (event) { @@ -316,12 +326,7 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath } }); - const worldLayer = app.scene.layers.getLayerByName("World"); - if (!worldLayer) throw new Error('World layer expected'); - - const mousePosition = new Float32Array(2); - - // Create a 2D screen + // Create a 2D screen for help text const screen = new pc.Entity(); screen.addComponent("screen", { referenceResolution: new pc.Vec2(1280, 720), @@ -331,7 +336,7 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath }); app.root.addChild(screen); - // Markup Text with wrap + // Help text const textMarkup = new pc.Entity(); textMarkup.setLocalPosition(0, 50, 0); textMarkup.addComponent("element", { @@ -351,12 +356,11 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath }); screen.addChild(textMarkup); - // update things every frame let passNum = 0; - app.on("update", function (/** @type {number} */dt) { + app.on("update", function (/** @type {number} */) { - mousePosition[0] = mousePos.x; - mousePosition[1] = mousePos.y; + mouseUniform[0] = mousePos.x; + mouseUniform[1] = mousePos.y; const brushRadius = data.get('options.brushSize') / Math.max(TEXTURE_WIDTH, TEXTURE_HEIGHT); const brush = data.get('options.brush') ?? 1; @@ -364,18 +368,20 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath // Run the sand simulation shader for (let i = 0; i < STEPS_PER_FRAME; i++) { device.scope.resolve('sourceTexture').setValue(sourceTexture); - device.scope.resolve('mousePosition').setValue(mousePosition); + device.scope.resolve('mousePosition').setValue(mouseUniform); device.scope.resolve('mouseButton').setValue(mouseState); device.scope.resolve('brush').setValue(brush); device.scope.resolve('brushRadius').setValue(brushRadius); device.scope.resolve('passNum').setValue(passNum); + device.scope.resolve('randomVal').setValue(Math.random()); pc.drawQuadWithShader(device, sandRenderTarget, sandShader); device.copyRenderTarget(sandRenderTarget, sourceRenderTarget, true, false); - passNum = (passNum + 1) % 3; + passNum = (passNum + 1) % 16; } + // Render a visual representation of the simulation device.scope.resolve('sourceTexture').setValue(sandRenderTarget.colorBuffer); - device.scope.resolve('mousePosition').setValue(mousePosition); + device.scope.resolve('mousePosition').setValue(mouseUniform); device.scope.resolve('brushRadius').setValue(brushRadius); pc.drawQuadWithShader(device, outputRenderTarget, outputShader); @@ -394,29 +400,24 @@ export class IntegerTextureExample { 'sandCommon.frag': /* glsl */` const uint AIR = 0u; const uint SAND = 1u; - const uint WALL = 2u; - const uint WATER = 3u; + const uint ORANGESAND = 2u; + const uint GRAYSAND = 3u; + const uint WALL = 4u; bool isInBounds(ivec2 c, ivec2 size) { return c.x > 0 && c.x < size.x - 1 && c.y > 0 && c.y < size.y - 1; } - int seed; - - float rand() { - int n = (seed++ << 13) ^ seed; - return float((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 2147483647.0; - } - - #define INIT_SEED() \ - seed = int(uv0.x + uv0.y + float(coord.x) + float(coord.y)); \ - seed = int(rand() * 2147483647.0) + int(passNum); - struct Particle { uint element; // 3 bits bool movedThisFrame; // 1 bit uint shade; // 4 bits + uint waterMass; // 8 bits }; + + float rand(vec2 pos, float val) { + return fract(pos.x * pos.y * val * 1000.0); + } uvec2 pack(Particle particle) { uint packed = 0u; @@ -424,7 +425,7 @@ export class IntegerTextureExample { packed |= ((particle.movedThisFrame ? 1u : 0u) << 3); // Store movedThisFrame in the next bit packed |= (particle.shade << 4); // Store shade in the next 4 bits - return uvec2(packed, 0u); // Second component is reserved/unused + return uvec2(packed, particle.waterMass); // Second component is reserved/unused } Particle unpack(uvec2 pixel) { @@ -434,6 +435,8 @@ export class IntegerTextureExample { particle.element = packed & 0x7u; // Extract lowest 3 bits particle.movedThisFrame = ((packed >> 3) & 0x1u) != 0u; // Extract the next bit particle.shade = (packed >> 4) & 0xFu; // Extract the next 4 bits + + particle.waterMass = pixel.y; return particle; } @@ -463,6 +466,7 @@ export class IntegerTextureExample { uniform uint mouseButton; uniform uint passNum; uniform uint brush; + uniform float randomVal; uniform float brushRadius; varying vec2 uv0; @@ -480,46 +484,37 @@ export class IntegerTextureExample { return; } - Particle currentParticle = getParticle(coord); - - Particle nextState = currentParticle; - nextState.movedThisFrame = false; float mouseDist = distance(mousePosition, uv0); - - INIT_SEED(); - int dir = int(passNum % 3u) - 1; - if (rand() > 0.75) { - dir = ((dir + 1) % 3) - 1; - } - int upDown = 1; - if (rand() > 0.99) { - upDown = 0; - } + Particle currentParticle = getParticle(coord); + Particle nextState = currentParticle; if (mouseButton == 1u && mouseDist < brushRadius) { nextState.element = brush; nextState.movedThisFrame = true; - nextState.shade = uint(rand() * 15.0); + nextState.shade = uint(rand(uv0, randomVal * float(passNum)) * 15.0); } else if (mouseButton == 2u && mouseDist < brushRadius) { nextState.element = AIR; nextState.movedThisFrame = false; - nextState.shade = uint(rand() * 15.0); - } else if (currentParticle.element == AIR) { - Particle particleAbove = getParticle(coord + ivec2(dir, -upDown)); - if (particleAbove.element == SAND) { - nextState = particleAbove; + nextState.shade = uint(rand(uv0, randomVal * float(passNum)) * 15.0); + } + + currentParticle.movedThisFrame = false; + if (currentParticle.element == AIR) { + Particle above = getParticle(coord + ivec2(dir, -1)); + if (above.element != AIR && above.element != WALL) { + nextState = above; nextState.movedThisFrame = true; } - } else if (currentParticle.element == SAND) { - Particle particleBelow = getParticle(coord + ivec2(-dir, upDown)); - if ((particleBelow.element == AIR) && !particleBelow.movedThisFrame) { - nextState = particleBelow; + } else if (currentParticle.element != WALL) { + Particle below = getParticle(coord + ivec2(-dir, 1)); + if (below.element == AIR && !below.movedThisFrame) { + nextState = below; nextState.movedThisFrame = false; } } - + gl_FragColor = pack(nextState); } `, @@ -533,6 +528,8 @@ export class IntegerTextureExample { vec3 whiteColor = vec3(1.0); vec3 skyBlueColor = vec3(0.6, 0.7, 0.8); vec3 yellowSandColor = vec3(0.73, 0.58, 0.26); + vec3 orangeSandColor = vec3(0.87, 0.43, 0.22); + vec3 graySandColor = vec3(0.13, 0.16, 0.17); vec3 grayWallColor = vec3(0.5, 0.5, 0.5); vec3 waterBlueColor = vec3(0.2, 0.3, 0.8); @@ -540,7 +537,7 @@ export class IntegerTextureExample { return length(p) - r; } - const float circleOutline = 0.01; + const float circleOutline = 0.0025; ${IntegerTextureExample.sharedShaderChunks['sandCommon.frag']} @@ -554,10 +551,13 @@ export class IntegerTextureExample { gameColor = mix(yellowSandColor, whiteColor, (float(particle.shade) / 15.0) * 0.5); } else if (particle.element == WALL) { gameColor = grayWallColor; - } else if (particle.element == WATER) { - gameColor = mix(waterBlueColor, whiteColor, (float(particle.shade) / 15.0) * 0.75); + } else if (particle.element == ORANGESAND) { + gameColor = mix(orangeSandColor, whiteColor, (float(particle.shade) / 15.0) * 0.5); + } else if (particle.element == GRAYSAND) { + gameColor = mix(graySandColor, whiteColor, (float(particle.shade) / 15.0) * 0.5); } + // Render a brush circle float d = length(uv0 - mousePosition); float wd = fwidth(d); float circle = smoothstep(brushRadius + wd, brushRadius, d); @@ -565,7 +565,6 @@ export class IntegerTextureExample { float brush = max(circle - circleInner, 0.0) * 0.5; vec3 outColor = mix(gameColor, vec3(1.0), brush); - //vec3 outColor = mix(vec3(1.0), gameColor, circle); gl_FragColor = vec4(outColor, 1.0); } From 58b8af5048892a9f83c0bf529167731193e551e8 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Sun, 14 Jan 2024 16:56:28 -0800 Subject: [PATCH 22/43] Clean up some lines --- src/platform/graphics/bind-group-format.js | 8 ++------ src/platform/graphics/shader-chunks/vert/webgpu.js | 1 - src/platform/graphics/webgpu/webgpu-shader.js | 2 -- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/platform/graphics/bind-group-format.js b/src/platform/graphics/bind-group-format.js index fd0a6316527..b57b27cffae 100644 --- a/src/platform/graphics/bind-group-format.js +++ b/src/platform/graphics/bind-group-format.js @@ -193,17 +193,13 @@ class BindGroupFormat { if (format.sampleType === SAMPLETYPE_INT) { textureType = `i${textureType}`; - // code += `layout(set = ${bindGroup}, binding = ${bindIndex++}) uniform ${textureType} ${format.name}${namePostfix};\n` + - // extraCode; } else if (format.sampleType === SAMPLETYPE_UINT) { textureType = `u${textureType}`; - // code += `layout(set = ${bindGroup}, binding = ${bindIndex++}) uniform ${textureType} ${format.name}${namePostfix};\n` + - // extraCode; } code += `layout(set = ${bindGroup}, binding = ${bindIndex++}) uniform ${textureType} ${format.name}${namePostfix};\n` + - `layout(set = ${bindGroup}, binding = ${bindIndex++}) uniform sampler ${format.name}_sampler;\n` + - extraCode; + `layout(set = ${bindGroup}, binding = ${bindIndex++}) uniform sampler ${format.name}_sampler;\n` + + extraCode; }); diff --git a/src/platform/graphics/shader-chunks/vert/webgpu.js b/src/platform/graphics/shader-chunks/vert/webgpu.js index 59150c6f19e..51106156247 100644 --- a/src/platform/graphics/shader-chunks/vert/webgpu.js +++ b/src/platform/graphics/shader-chunks/vert/webgpu.js @@ -6,7 +6,6 @@ export default /* glsl */` #define texture2D(res, uv) texture(sampler2D(res, res ## _sampler), uv) #define itexture2D(res, uv) texture(isampler2D(res, res ## _sampler), uv) #define utexture2D(res, uv) texture(usampler2D(res, res ## _sampler), uv) -#define texelFetch(res, uv) textureLoad(res, vec2i(uv), 0) #define GL2 #define WEBGPU diff --git a/src/platform/graphics/webgpu/webgpu-shader.js b/src/platform/graphics/webgpu/webgpu-shader.js index 3ad6ee191d6..565a1ce9172 100644 --- a/src/platform/graphics/webgpu/webgpu-shader.js +++ b/src/platform/graphics/webgpu/webgpu-shader.js @@ -126,8 +126,6 @@ class WebgpuShader { this.processed = processed; }); - console.log("Processing shader", shader); - this._vertexCode = this.transpile(processed.vshader, 'vertex', shader.definition.vshader); this._fragmentCode = this.transpile(processed.fshader, 'fragment', shader.definition.fshader); From 450c4fcc5170aa3594dac0381fa43ab507ce594c Mon Sep 17 00:00:00 2001 From: Liam Don Date: Sun, 14 Jan 2024 17:00:59 -0800 Subject: [PATCH 23/43] Use single int texture instead of uvec2 --- .../examples/graphics/integer-textures.mjs | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/examples/src/examples/graphics/integer-textures.mjs b/examples/src/examples/graphics/integer-textures.mjs index fb9caacb9d4..01ba78f7b99 100644 --- a/examples/src/examples/graphics/integer-textures.mjs +++ b/examples/src/examples/graphics/integer-textures.mjs @@ -115,7 +115,11 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath name: `PixelBuffer_${i}`, width: TEXTURE_WIDTH, height: TEXTURE_HEIGHT, - format: pc.PIXELFORMAT_RG8UI, + // Note that we are using an unsigned integer format here. + // This can be helpful for storing bitfields in each pixel. + // In this example, we are storing 3 different properties + // in a single Uint8 value. + format: pc.PIXELFORMAT_R8UI, addressU: pc.ADDRESS_CLAMP_TO_EDGE, addressV: pc.ADDRESS_CLAMP_TO_EDGE }); @@ -160,7 +164,7 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath 'SandShader', { aPosition: pc.SEMANTIC_POSITION }, false, - 'uvec2' + 'uint' ); // This shader reads the integer textures @@ -178,7 +182,7 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath const sourceTextureData = sourceTexture.lock(); for (let x = 0; x < sourceTexture.width; x++) { for (let y = 0; y < sourceTexture.height; y++) { - const i = (y * sourceTexture.width + x) * 2; + const i = (y * sourceTexture.width + x); if (x > sourceTexture.width * 0.3 && x < sourceTexture.width * 0.7 && y > sourceTexture.height * 0.7 && y < sourceTexture.height * 0.8) { sourceTextureData[i] = 4; sourceTextureData[i] |= (Math.floor(Math.random() * 15) << 4); @@ -419,30 +423,25 @@ export class IntegerTextureExample { return fract(pos.x * pos.y * val * 1000.0); } - uvec2 pack(Particle particle) { + uint pack(Particle particle) { uint packed = 0u; packed |= (particle.element & 0x7u); // Store element in the lowest 3 bits packed |= ((particle.movedThisFrame ? 1u : 0u) << 3); // Store movedThisFrame in the next bit packed |= (particle.shade << 4); // Store shade in the next 4 bits - return uvec2(packed, particle.waterMass); // Second component is reserved/unused + return packed; // Second component is reserved/unused } - Particle unpack(uvec2 pixel) { - uint packed = pixel.x; - + Particle unpack(uint packed) { Particle particle; particle.element = packed & 0x7u; // Extract lowest 3 bits particle.movedThisFrame = ((packed >> 3) & 0x1u) != 0u; // Extract the next bit - particle.shade = (packed >> 4) & 0xFu; // Extract the next 4 bits - - particle.waterMass = pixel.y; - + particle.shade = (packed >> 4) & 0xFu; // Extract the next 4 bits return particle; } Particle getParticle(ivec2 c) { - uvec2 val = texelFetch(sourceTexture, c, 0).rg; + uint val = texelFetch(sourceTexture, c, 0).r; return unpack(val); } ` @@ -479,8 +478,7 @@ export class IntegerTextureExample { ivec2 coord = ivec2(uv0 * vec2(size)); if (!isInBounds(coord, size)) { - //gl_FragColor = uvec2(WALL, 0u); - gl_FragColor = uvec2(WALL, 0u); + gl_FragColor = WALL; return; } From ed815d97639588162baf272ffb6aea51ca835b91 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Sun, 14 Jan 2024 17:10:48 -0800 Subject: [PATCH 24/43] Add additional keyboard shortcuts --- .../examples/graphics/integer-textures.mjs | 45 +++++++++++++++---- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/examples/src/examples/graphics/integer-textures.mjs b/examples/src/examples/graphics/integer-textures.mjs index 01ba78f7b99..41eb48c6c98 100644 --- a/examples/src/examples/graphics/integer-textures.mjs +++ b/examples/src/examples/graphics/integer-textures.mjs @@ -288,16 +288,39 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath // Setup mouse controls const mouse = new pc.Mouse(document.body); - const lookRange = 1.5; - const mouseRay = new pc.Ray(); - const planePoint = new pc.Vec3(); - const mousePos = new pc.Vec2(); - const mouseUniform = new Float32Array(2); - let mouseState = 0; + const keyboard = new pc.Keyboard(document.body); + mouse.disableContextMenu(); + + // Reset on space bar, select brush on 1-4 + keyboard.on(pc.EVENT_KEYUP, (event) => { + switch (event.key) { + case pc.KEY_SPACE: + resetData(); + break; + case pc.KEY_1: + data.set('options.brush', 1); + break; + case pc.KEY_2: + data.set('options.brush', 2); + break; + case pc.KEY_3: + data.set('options.brush', 3); + break; + case pc.KEY_4: + data.set('options.brush', 4); + break; + } + }, this); + + let mouseState = 0; mouse.on(pc.EVENT_MOUSEDOWN, function (event) { if (event.button === pc.MOUSEBUTTON_LEFT) { - mouseState = 1; + if (keyboard.isPressed(pc.KEY_SHIFT)) { + mouseState = 2; + } else { + mouseState = 1; + } } else if (event.button === pc.MOUSEBUTTON_RIGHT) { mouseState = 2; } @@ -305,6 +328,12 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath mouse.on(pc.EVENT_MOUSEUP, function () { mouseState = 0; }); + + const lookRange = 1.5; + const mouseRay = new pc.Ray(); + const planePoint = new pc.Vec3(); + const mousePos = new pc.Vec2(); + const mouseUniform = new Float32Array(2); mouse.on(pc.EVENT_MOUSEMOVE, function (event) { const x = event.x; const y = event.y; @@ -349,7 +378,7 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath anchor: new pc.Vec4(0.25, 0.01, 0.96, 0.99), fontAsset: assets.font.id, fontSize: 16, - text: 'Left click: add\nRight click: remove\nPress space: reset', + text: 'Left click: Add\nShift click: Remove\nPress Space: Reset', width: 500, height: 100, autoWidth: false, From 2072c1dbb2c2b21df24500c88f284935ef2b02da Mon Sep 17 00:00:00 2001 From: Liam Don Date: Mon, 15 Jan 2024 08:44:13 -0800 Subject: [PATCH 25/43] Reuse vertex shader, add comment --- .../src/examples/graphics/integer-textures.mjs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/examples/src/examples/graphics/integer-textures.mjs b/examples/src/examples/graphics/integer-textures.mjs index 41eb48c6c98..d5dc319091c 100644 --- a/examples/src/examples/graphics/integer-textures.mjs +++ b/examples/src/examples/graphics/integer-textures.mjs @@ -159,19 +159,19 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath // It uses integer textures to store the state of each pixel const sandShader = pc.createShaderFromCode( device, - files['quad.vert'], + pc.RenderPassShaderQuad.quadVertexShader, files['sandSimulation.frag'], 'SandShader', { aPosition: pc.SEMANTIC_POSITION }, false, - 'uint' + 'uint' // The usual output format of a shader is vec4, but we change it to uint ); // This shader reads the integer textures // and renders a visual representation of the simulation const outputShader = pc.createShaderFromCode( device, - files['quad.vert'], + pc.RenderPassShaderQuad.quadVertexShader, files['renderOutput.frag'], 'RenderOutputShader', { aPosition: pc.SEMANTIC_POSITION } @@ -477,15 +477,6 @@ export class IntegerTextureExample { }; static FILES = { - 'quad.vert': /* glsl */` - attribute vec2 aPosition; - varying vec2 uv0; - void main(void) - { - gl_Position = vec4(aPosition, 0.0, 1.0); - uv0 = getImageEffectUV((aPosition.xy + 1.0) * 0.5); - } - `, 'sandSimulation.frag': /* glsl */` precision highp usampler2D; From a418af4688e6e92f402bd97178ef6bac30a0b363 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Mon, 15 Jan 2024 09:07:07 -0800 Subject: [PATCH 26/43] Add comments to resetData --- .../src/examples/graphics/integer-textures.mjs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/examples/src/examples/graphics/integer-textures.mjs b/examples/src/examples/graphics/integer-textures.mjs index d5dc319091c..b5efe0e6202 100644 --- a/examples/src/examples/graphics/integer-textures.mjs +++ b/examples/src/examples/graphics/integer-textures.mjs @@ -179,17 +179,29 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath // Write the initial simulation state to the integer texture const resetData = () => { + // Loop through the pixels in the texture + // and initialize them to either AIR, SAND or WALL const sourceTextureData = sourceTexture.lock(); for (let x = 0; x < sourceTexture.width; x++) { for (let y = 0; y < sourceTexture.height; y++) { const i = (y * sourceTexture.width + x); - if (x > sourceTexture.width * 0.3 && x < sourceTexture.width * 0.7 && y > sourceTexture.height * 0.7 && y < sourceTexture.height * 0.8) { + + const isDefaultWall = x > sourceTexture.width * 0.3 && x < sourceTexture.width * 0.7 && y > sourceTexture.height * 0.7 && y < sourceTexture.height * 0.8; + + if (isDefaultWall) { // Create the default wall in the middle of the screen + // The WALL element is used to mark pixels that should not be moved + // It uses the integer '4' (see sandCommon.frag) sourceTextureData[i] = 4; - sourceTextureData[i] |= (Math.floor(Math.random() * 15) << 4); - } else if (Math.random() > 0.94) { + } else if (Math.random() > 0.94) { // Sprinkle some sand randomly around the scene + // The SAND element is used to mark pixels that fall like sand + // It uses the integer '1' (see sandCommon.frag) sourceTextureData[i] = 1; + // The shade of each pixel is stored in the upper 4 bits of the integer + // Here we write a random value to the shade bits sourceTextureData[i] |= (Math.floor(Math.random() * 15) << 4); } else { + // The AIR element is used to mark pixels that are empty + // Other than the wall and sand, all pixels are initialized to AIR sourceTextureData[i] = 0; } } From 893b5cac924dd70857419e689c93445eee33ba58 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Mon, 15 Jan 2024 09:07:52 -0800 Subject: [PATCH 27/43] Remove undefined throws --- examples/src/examples/graphics/integer-textures.mjs | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/src/examples/graphics/integer-textures.mjs b/examples/src/examples/graphics/integer-textures.mjs index b5efe0e6202..654811f168a 100644 --- a/examples/src/examples/graphics/integer-textures.mjs +++ b/examples/src/examples/graphics/integer-textures.mjs @@ -233,7 +233,6 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath cameraEntity.setPosition(0, 5, 15); cameraEntity.lookAt(0, 5, 0); app.root.addChild(cameraEntity); - if (cameraEntity.camera === undefined) throw new Error('Camera component expected'); // create material used on the ground plane const groundMaterial = new pc.StandardMaterial(); @@ -283,7 +282,6 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath lightmapped: false, type: "plane" }); - if (!gameScreen.render) throw new Error('Render component expected'); gameScreen.setLocalPosition(0, 5, 0); gameScreen.setLocalScale(PLANE_WIDTH, 1, PLANE_HEIGHT); gameScreen.setEulerAngles(90, 0, 0); From 0a5e4f29c976253c970e46e449c3ae9516555cd5 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Mon, 15 Jan 2024 09:18:47 -0800 Subject: [PATCH 28/43] Use example description, remove screen and assets, add comments --- .../examples/graphics/integer-textures.mjs | 57 +++++-------------- examples/src/static/styles.css | 2 +- 2 files changed, 15 insertions(+), 44 deletions(-) diff --git a/examples/src/examples/graphics/integer-textures.mjs b/examples/src/examples/graphics/integer-textures.mjs index 654811f168a..78f1ef44974 100644 --- a/examples/src/examples/graphics/integer-textures.mjs +++ b/examples/src/examples/graphics/integer-textures.mjs @@ -49,6 +49,16 @@ function controls({ observer, ReactPCUI, jsx, fragment }) { * @returns {Promise} The example application. */ async function example({ canvas, data, deviceType, assetPath, files, glslangPath, twgslPath, dracoPath }) { + // + // In this example, integer textures are used to store the state of each pixel in a simulation. + // The simulation is run in a shader, and the results are rendered to a texture. + // + // Integer textures can be useful for "compute-like" use cases, where you want to store + // arbitrary data in each pixel, and then use a shader to process the data. + // + // This example uses integer textures instead of floats in order to store + // multiple properties (element, shade, movedThisFrame) in the bits of each pixel. + // const STEPS_PER_FRAME = 4; const PLANE_WIDTH = 10; @@ -65,10 +75,7 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath fallbackUrl: dracoPath + 'draco.js' }); - const assets = { - noiseTexture: new pc.Asset('noise', 'texture', { url: assetPath + 'textures/clouds.jpg' }), - font: new pc.Asset('font', 'font', { url: assetPath + 'fonts/courier.json' }) - }; + const assets = {}; const gfxOptions = { deviceTypes: [deviceType], @@ -84,16 +91,9 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath createOptions.componentSystems = [ pc.RenderComponentSystem, pc.CameraComponentSystem, - pc.LightComponentSystem, - pc.ScreenComponentSystem, - pc.ElementComponentSystem - ]; - createOptions.resourceHandlers = [ - // @ts-ignore - pc.TextureHandler, - // @ts-ignore - pc.FontHandler + pc.LightComponentSystem ]; + createOptions.resourceHandlers = []; const app = new pc.AppBase(canvas); app.init(createOptions); @@ -369,36 +369,6 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath } }); - // Create a 2D screen for help text - const screen = new pc.Entity(); - screen.addComponent("screen", { - referenceResolution: new pc.Vec2(1280, 720), - scaleBlend: 0.5, - scaleMode: pc.SCALEMODE_BLEND, - screenSpace: true - }); - app.root.addChild(screen); - - // Help text - const textMarkup = new pc.Entity(); - textMarkup.setLocalPosition(0, 50, 0); - textMarkup.addComponent("element", { - alignment: new pc.Vec2(1.0, 0.0), - pivot: new pc.Vec2(0.0, 0.0), - anchor: new pc.Vec4(0.25, 0.01, 0.96, 0.99), - fontAsset: assets.font.id, - fontSize: 16, - text: 'Left click: Add\nShift click: Remove\nPress Space: Reset', - width: 500, - height: 100, - autoWidth: false, - autoHeight: false, - wrapLines: true, - enableMarkup: true, - type: pc.ELEMENTTYPE_TEXT - }); - screen.addChild(textMarkup); - let passNum = 0; app.on("update", function (/** @type {number} */) { @@ -437,6 +407,7 @@ export class IntegerTextureExample { static CATEGORY = 'Graphics'; static WEBGPU_ENABLED = true; static WEBGL1_DISABLED = true; + static DESCRIPTION = `
  • Click to add sand
  • Shift-click to remove sand
  • Press space to reset.
`; static example = example; static controls = controls; static sharedShaderChunks = { diff --git a/examples/src/static/styles.css b/examples/src/static/styles.css index 2dd1110871b..fa47333776c 100644 --- a/examples/src/static/styles.css +++ b/examples/src/static/styles.css @@ -408,7 +408,7 @@ body { bottom: 8px; transform: translateX(-50%); color: #f2f2f2; - width: 50%; + width: 60%; } #descriptionPanel.mobile { From 1757afcb10f4ee20eaac271424107d8f368bff8d Mon Sep 17 00:00:00 2001 From: Liam Don Date: Mon, 15 Jan 2024 09:19:32 -0800 Subject: [PATCH 29/43] Revert description width change --- examples/src/static/styles.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/static/styles.css b/examples/src/static/styles.css index fa47333776c..2dd1110871b 100644 --- a/examples/src/static/styles.css +++ b/examples/src/static/styles.css @@ -408,7 +408,7 @@ body { bottom: 8px; transform: translateX(-50%); color: #f2f2f2; - width: 60%; + width: 50%; } #descriptionPanel.mobile { From 3c87f7fc9bc69d5ed4edb175190274041a61f61c Mon Sep 17 00:00:00 2001 From: Liam Don Date: Mon, 15 Jan 2024 10:00:38 -0800 Subject: [PATCH 30/43] Add a WebGL1 warning in controls panel --- .../scripts/generate-standalone-files.mjs | 6 +--- .../examples/graphics/integer-textures.mjs | 31 +++++++++++++------ src/platform/graphics/constants.js | 3 +- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/examples/scripts/generate-standalone-files.mjs b/examples/scripts/generate-standalone-files.mjs index d84c55eb5f5..34d09ca291f 100644 --- a/examples/scripts/generate-standalone-files.mjs +++ b/examples/scripts/generate-standalone-files.mjs @@ -136,10 +136,6 @@ ${exampleClass.example.toString()} console.warn('Picked WebGPU but example is not supported on WebGPU, defaulting to WebGL2'); return 'webgl2'; } - if (last === 'webgl1' && ${exampleClass.WEBGL1_DISABLED === true}) { - console.warn('Picked WebGL1 but example is not supported on WebGL1, defaulting to WebGL2'); - return 'webgl2'; - } return last; } else if (${Boolean(exampleClass.WEBGPU_ENABLED)}) { let preferredDevice = 'webgpu'; @@ -148,7 +144,7 @@ ${exampleClass.example.toString()} preferredDevice = 'webgl2'; } return window.top.preferredGraphicsDevice || preferredDevice; - } else if (['webgl1', 'webgl2'].includes(window.top.preferredGraphicsDevice) && !${Boolean(exampleClass.WEBGL1_DISABLED)}) { + } else if (['webgl1', 'webgl2'].includes(window.top.preferredGraphicsDevice)) { return window.top.preferredGraphicsDevice; } else { return 'webgl2'; diff --git a/examples/src/examples/graphics/integer-textures.mjs b/examples/src/examples/graphics/integer-textures.mjs index 78f1ef44974..6860dedfc17 100644 --- a/examples/src/examples/graphics/integer-textures.mjs +++ b/examples/src/examples/graphics/integer-textures.mjs @@ -5,8 +5,15 @@ import * as pc from 'playcanvas'; * @returns {JSX.Element} The returned JSX Element. */ function controls({ observer, ReactPCUI, jsx, fragment }) { - const { BindingTwoWay, Container, Button, LabelGroup, Panel, SliderInput, SelectInput } = ReactPCUI; + const { BindingTwoWay, Container, Button, InfoBox, LabelGroup, Panel, SliderInput, SelectInput } = ReactPCUI; + return fragment( + jsx(InfoBox, { + icon: 'E218', + title: 'WebGL 1.0', + text: 'Integer textures are not supported on WebGL 1.0 devices', + hidden: !(pc.app?.graphicsDevice.isWebGL1 ?? false) + }), jsx(Panel, { headerText: 'Sand simulation' }, jsx(LabelGroup, { text: 'Brush' }, jsx(SelectInput, { @@ -48,7 +55,7 @@ function controls({ observer, ReactPCUI, jsx, fragment }) { * @param {Options} options - The example options. * @returns {Promise} The example application. */ -async function example({ canvas, data, deviceType, assetPath, files, glslangPath, twgslPath, dracoPath }) { +async function example({ canvas, data, deviceType, files, glslangPath, twgslPath, dracoPath }) { // // In this example, integer textures are used to store the state of each pixel in a simulation. // The simulation is run in a shader, and the results are rendered to a texture. @@ -130,12 +137,15 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath colorBuffer: colorBuffer }); }; + // Create our integer pixel buffers and render targets - const pixelColorBuffers = [createPixelColorBuffer(0), createPixelColorBuffer(1)]; - const pixelRenderTargets = [ - createPixelRenderTarget(0, pixelColorBuffers[0]), - createPixelRenderTarget(1, pixelColorBuffers[1]) - ]; + const pixelColorBuffers = []; + const pixelRenderTargets = []; + if (!device.isWebGL1) { + pixelColorBuffers.push(createPixelColorBuffer(0), createPixelColorBuffer(1)); + pixelRenderTargets.push(createPixelRenderTarget(0, pixelColorBuffers[0])); + pixelRenderTargets.push(createPixelRenderTarget(1, pixelColorBuffers[1])); + } const sourceTexture = pixelColorBuffers[0]; const sourceRenderTarget = pixelRenderTargets[0]; @@ -179,6 +189,7 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath // Write the initial simulation state to the integer texture const resetData = () => { + if (device.isWebGL1) return; // Loop through the pixels in the texture // and initialize them to either AIR, SAND or WALL const sourceTextureData = sourceTexture.lock(); @@ -214,7 +225,6 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath const assetListLoader = new pc.AssetListLoader(Object.values(assets), app.assets); assetListLoader.load(() => { - data.set('options', { brush: 1, brushSize: 8 @@ -371,6 +381,10 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath let passNum = 0; app.on("update", function (/** @type {number} */) { + if (device.isWebGL1) { + // WebGL1 does not support integer textures + return; + } mouseUniform[0] = mousePos.x; mouseUniform[1] = mousePos.y; @@ -406,7 +420,6 @@ async function example({ canvas, data, deviceType, assetPath, files, glslangPath export class IntegerTextureExample { static CATEGORY = 'Graphics'; static WEBGPU_ENABLED = true; - static WEBGL1_DISABLED = true; static DESCRIPTION = `
  • Click to add sand
  • Shift-click to remove sand
  • Press space to reset.
`; static example = example; static controls = controls; diff --git a/src/platform/graphics/constants.js b/src/platform/graphics/constants.js index 45b1eb239b4..1813a591f38 100644 --- a/src/platform/graphics/constants.js +++ b/src/platform/graphics/constants.js @@ -1453,8 +1453,7 @@ export const uniformTypeToName = [ 'usampler2DArray' ]; -// Map used in uniform-buffer.js to convert uniform type to storage type -// used in the uniform buffer. +// Map to convert uniform type to storage type, used in uniform-buffer.js export const uniformTypeToStorage = new Uint8Array([ TYPE_INT32, // UNIFORMTYPE_BOOL TYPE_INT32, // UNIFORMTYPE_INT From c4d3a8f157aa9b5e5003c4c0e13e6c4918abf70b Mon Sep 17 00:00:00 2001 From: Liam Don Date: Mon, 15 Jan 2024 10:58:33 -0800 Subject: [PATCH 31/43] Accept options object when creating shaders, allow changing outputType for every attachment --- .../examples/graphics/integer-textures.mjs | 10 ++++- .../graphics/shader-chunks/frag/gles3.js | 20 ++++----- .../graphics/shader-chunks/frag/webgpu.js | 45 ++++++++++++++----- src/platform/graphics/shader-utils.js | 24 ++++++---- src/platform/graphics/shader.js | 1 + src/scene/shader-lib/utils.js | 26 ++++++----- 6 files changed, 82 insertions(+), 44 deletions(-) diff --git a/examples/src/examples/graphics/integer-textures.mjs b/examples/src/examples/graphics/integer-textures.mjs index 6860dedfc17..e463f4b432f 100644 --- a/examples/src/examples/graphics/integer-textures.mjs +++ b/examples/src/examples/graphics/integer-textures.mjs @@ -173,8 +173,12 @@ async function example({ canvas, data, deviceType, files, glslangPath, twgslPath files['sandSimulation.frag'], 'SandShader', { aPosition: pc.SEMANTIC_POSITION }, - false, - 'uint' // The usual output format of a shader is vec4, but we change it to uint + // Note that we are changing the shader output type to 'uint' + // This means we only have to return a single integer value from the shader, + // whereas the default is to return a vec4. This option allows you to pass + // an array of types to specify the output type for each color attachment. + // Unspecified types are assumed to be 'vec4'. + { fragmentOutputTypes: ['uint'] } ); // This shader reads the integer textures @@ -185,6 +189,8 @@ async function example({ canvas, data, deviceType, files, glslangPath, twgslPath files['renderOutput.frag'], 'RenderOutputShader', { aPosition: pc.SEMANTIC_POSITION } + // For the output shader, we don't need to specify the output type, + // as we are returning a vec4 by default. ); // Write the initial simulation state to the integer texture diff --git a/src/platform/graphics/shader-chunks/frag/gles3.js b/src/platform/graphics/shader-chunks/frag/gles3.js index e63c7a064be..a6a211af7dd 100644 --- a/src/platform/graphics/shader-chunks/frag/gles3.js +++ b/src/platform/graphics/shader-chunks/frag/gles3.js @@ -1,50 +1,50 @@ export default /* glsl */` -#ifndef outType -#define outType vec4 +#ifndef outType_0 +#define outType_0 vec4 #endif -layout(location = 0) out highp outType pc_fragColor; +layout(location = 0) out highp outType_0 pc_fragColor; #ifndef REMOVE_COLOR_ATTACHMENT_1 #if COLOR_ATTACHMENT_1 -layout(location = 1) out highp outType pc_fragColor1; +layout(location = 1) out highp outType_1 pc_fragColor1; #endif #endif #ifndef REMOVE_COLOR_ATTACHMENT_2 #if COLOR_ATTACHMENT_2 -layout(location = 2) out highp outType pc_fragColor2; +layout(location = 2) out highp outType_2 pc_fragColor2; #endif #endif #ifndef REMOVE_COLOR_ATTACHMENT_3 #if COLOR_ATTACHMENT_3 -layout(location = 3) out highp outType pc_fragColor3; +layout(location = 3) out highp outType_3 pc_fragColor3; #endif #endif #ifndef REMOVE_COLOR_ATTACHMENT_4 #if COLOR_ATTACHMENT_4 -layout(location = 4) out highp outType pc_fragColor4; +layout(location = 4) out highp outType_4 pc_fragColor4; #endif #endif #ifndef REMOVE_COLOR_ATTACHMENT_5 #if COLOR_ATTACHMENT_5 -layout(location = 5) out highp outType pc_fragColor5; +layout(location = 5) out highp outType_5 pc_fragColor5; #endif #endif #ifndef REMOVE_COLOR_ATTACHMENT_6 #if COLOR_ATTACHMENT_6 -layout(location = 6) out highp outType pc_fragColor6; +layout(location = 6) out highp outType_6 pc_fragColor6; #endif #endif #ifndef REMOVE_COLOR_ATTACHMENT_7 #if COLOR_ATTACHMENT_7 -layout(location = 7) out highp outType pc_fragColor7; +layout(location = 7) out highp outType_7 pc_fragColor7; #endif #endif diff --git a/src/platform/graphics/shader-chunks/frag/webgpu.js b/src/platform/graphics/shader-chunks/frag/webgpu.js index 6af67b2e2f9..45f0b780098 100644 --- a/src/platform/graphics/shader-chunks/frag/webgpu.js +++ b/src/platform/graphics/shader-chunks/frag/webgpu.js @@ -3,18 +3,39 @@ export default /* glsl */` // texelFetch support and others #extension GL_EXT_samplerless_texture_functions : require -#ifndef outType -#define outType vec4 -#endif - -layout(location = 0) out highp outType pc_fragColor; -layout(location = 1) out highp outType pc_fragColor1; -layout(location = 2) out highp outType pc_fragColor2; -layout(location = 3) out highp outType pc_fragColor3; -layout(location = 4) out highp outType pc_fragColor4; -layout(location = 5) out highp outType pc_fragColor5; -layout(location = 6) out highp outType pc_fragColor6; -layout(location = 7) out highp outType pc_fragColor7; +#ifndef outType_0 +#define outType_0 vec4 +#endif +#ifndef outType_1 +#define outType_1 vec4 +#endif +#ifndef outType_2 +#define outType_2 vec4 +#endif +#ifndef outType_3 +#define outType_3 vec4 +#endif +#ifndef outType_4 +#define outType_4 vec4 +#endif +#ifndef outType_5 +#define outType_5 vec4 +#endif +#ifndef outType_6 +#define outType_6 vec4 +#endif +#ifndef outType_7 +#define outType_7 vec4 +#endif + +layout(location = 0) out highp outType_0 pc_fragColor; +layout(location = 1) out highp outType_1 pc_fragColor1; +layout(location = 2) out highp outType_2 pc_fragColor2; +layout(location = 3) out highp outType_3 pc_fragColor3; +layout(location = 4) out highp outType_4 pc_fragColor4; +layout(location = 5) out highp outType_5 pc_fragColor5; +layout(location = 6) out highp outType_6 pc_fragColor6; +layout(location = 7) out highp outType_7 pc_fragColor7; #define gl_FragColor pc_fragColor diff --git a/src/platform/graphics/shader-utils.js b/src/platform/graphics/shader-utils.js index 6e2075f8ec8..5831ff356f0 100644 --- a/src/platform/graphics/shader-utils.js +++ b/src/platform/graphics/shader-utils.js @@ -50,9 +50,8 @@ class ShaderUtils { * @param {string} [options.fragmentDefines] - The fragment shader defines. * @param {string} [options.fragmentExtensions] - The fragment shader extensions code. * @param {string} [options.fragmentPreamble] - The preamble string for the fragment shader. - * @param {boolean} [options.useTransformFeedback] - Whether to use transform feedback. Defaults - * @param {string} [options.fragmentOutputType] - Output fragment shader type. Defaults to vec4. - * to false. + * @param {boolean} [options.useTransformFeedback] - Whether to use transform feedback. Defaults to false + * @param {string | string[]} [options.fragmentOutputTypes] - Fragment shader output types, which default to vec4. Passing a string will set the output type for all color attachments. Passing an array will set the output type for each color attachment. * @returns {object} Returns the created shader definition. */ static createDefinition(device, options) { @@ -66,15 +65,24 @@ class ShaderUtils { // a define per supported color attachment, which strips out unsupported output definitions in the deviceIntro let attachmentsDefine = ''; + // Normalize fragmentOutputTypes to an array + let fragmentOutputTypes = options.fragmentOutputTypes ?? ['vec4']; + if (!Array.isArray(fragmentOutputTypes)) { + fragmentOutputTypes = [fragmentOutputTypes ?? 'vec4']; + } + // Define the fragment shader output type, vec4 by default if (!isVertex) { - const outType = options.fragmentOutputType ?? 'vec4'; - attachmentsDefine += `#define outType ${outType}\n`; + let outType = ''; + for (let i = 0; i < device.maxColorAttachments; i++) { + attachmentsDefine += `#define COLOR_ATTACHMENT_${i}\n`; + outType = fragmentOutputTypes[i] ?? 'vec4'; + attachmentsDefine += `#define outType_${i} ${outType}\n`; + } } - for (let i = 0; i < device.maxColorAttachments; i++) { - attachmentsDefine += `#define COLOR_ATTACHMENT_${i}\n`; - } + console.log("fragmentOutputTypes", fragmentOutputTypes); + console.log("attachmentsDefine", attachmentsDefine); return attachmentsDefine + deviceIntro; }; diff --git a/src/platform/graphics/shader.js b/src/platform/graphics/shader.js index ef199855079..6a122f6fe7f 100644 --- a/src/platform/graphics/shader.js +++ b/src/platform/graphics/shader.js @@ -54,6 +54,7 @@ class Shader { * WebGPU platform. * @param {boolean} [definition.useTransformFeedback] - Specifies that this shader outputs * post-VS data to a buffer. + * @param {string | string[]} [definition.fragmentOutputTypes] - Fragment shader output types, which default to vec4. Passing a string will set the output type for all color attachments. Passing an array will set the output type for each color attachment. * @param {string} [definition.shaderLanguage] - Specifies the shader language of vertex and * fragment shaders. Defaults to {@link SHADERLANGUAGE_GLSL}. * @example diff --git a/src/scene/shader-lib/utils.js b/src/scene/shader-lib/utils.js index eaaed4ae081..69203dbcee3 100644 --- a/src/scene/shader-lib/utils.js +++ b/src/scene/shader-lib/utils.js @@ -12,17 +12,18 @@ import { ShaderGenerator } from './programs/shader-generator.js'; * graphics device. * @param {string} vsName - The vertex shader chunk name. * @param {string} fsName - The fragment shader chunk name. - * @param {boolean} [useTransformFeedback] - Whether to use transform feedback. Defaults to false. - * @param {string} [fragmentOutputType] - Output fragment shader type. Defaults to vec4. + * @param {object} [shaderDefinitionOptions] - Additional options that will be added to the shader definition. + * @param {boolean} [shaderDefinitionOptions.useTransformFeedback] - Whether to use transform feedback. Defaults to false. + * @param {string | string[]} [shaderDefinitionOptions.fragmentOutputTypes] - Fragment shader output types, which default to vec4. Passing a string will set the output type for all color attachments. Passing an array will set the output type for each color attachment. + * @see ShaderUtils.createDefinition * @returns {Shader} The newly created shader. */ -function createShader(device, vsName, fsName, useTransformFeedback = false, fragmentOutputType = 'vec4') { +function createShader(device, vsName, fsName, shaderDefinitionOptions) { return new Shader(device, ShaderUtils.createDefinition(device, { + ...shaderDefinitionOptions, name: `${vsName}_${fsName}`, vertexCode: shaderChunks[vsName], - fragmentCode: shaderChunks[fsName], - useTransformFeedback: useTransformFeedback, - fragmentOutputType + fragmentCode: shaderChunks[fsName] })); } @@ -41,11 +42,13 @@ function createShader(device, vsName, fsName, useTransformFeedback = false, frag * @param {Object} [attributes] - Object detailing the mapping of vertex shader * attribute names to semantics SEMANTIC_*. This enables the engine to match vertex buffer data as * inputs to the shader. Defaults to undefined, which generates the default attributes. - * @param {boolean} [useTransformFeedback] - Whether to use transform feedback. Defaults to false. - * @param {string} [fragmentOutputType] - Output fragment shader type. Defaults to vec4. + * @param {object} [shaderDefinitionOptions] - Additional options that will be added to the shader definition. + * @param {boolean} [shaderDefinitionOptions.useTransformFeedback] - Whether to use transform feedback. Defaults to false. + * @param {string | string[]} [shaderDefinitionOptions.fragmentOutputTypes] - Fragment shader output types, which default to vec4. Passing a string will set the output type for all color attachments. Passing an array will set the output type for each color attachment. + * @see ShaderUtils.createDefinition * @returns {Shader} The newly created shader. */ -function createShaderFromCode(device, vsCode, fsCode, uniqueName, attributes, useTransformFeedback = false, fragmentOutputType = 'vec4') { +function createShaderFromCode(device, vsCode, fsCode, uniqueName, attributes, shaderDefinitionOptions) { // the function signature has changed, fail if called incorrectly Debug.assert(typeof attributes !== 'boolean'); @@ -54,12 +57,11 @@ function createShaderFromCode(device, vsCode, fsCode, uniqueName, attributes, us let shader = programLibrary.getCachedShader(uniqueName); if (!shader) { shader = new Shader(device, ShaderUtils.createDefinition(device, { + ...shaderDefinitionOptions, name: uniqueName, vertexCode: vsCode, fragmentCode: fsCode, - attributes: attributes, - useTransformFeedback: useTransformFeedback, - fragmentOutputType + attributes: attributes })); programLibrary.setCachedShader(uniqueName, shader); } From 63febb9af1ea7b578b0d78327bfaa714ea6285b7 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Mon, 15 Jan 2024 11:00:13 -0800 Subject: [PATCH 32/43] Remove console log --- src/platform/graphics/shader-utils.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/platform/graphics/shader-utils.js b/src/platform/graphics/shader-utils.js index 5831ff356f0..f8b08aa8764 100644 --- a/src/platform/graphics/shader-utils.js +++ b/src/platform/graphics/shader-utils.js @@ -81,9 +81,6 @@ class ShaderUtils { } } - console.log("fragmentOutputTypes", fragmentOutputTypes); - console.log("attachmentsDefine", attachmentsDefine); - return attachmentsDefine + deviceIntro; }; From d4bd68990098593f87bbb4d6334102e5415a4b77 Mon Sep 17 00:00:00 2001 From: Martin Valigursky <59932779+mvaligursky@users.noreply.github.com> Date: Tue, 16 Jan 2024 11:58:45 +0000 Subject: [PATCH 33/43] Update src/platform/graphics/shader-utils.js --- src/platform/graphics/shader-utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/graphics/shader-utils.js b/src/platform/graphics/shader-utils.js index f8b08aa8764..b3aaf5a7c0a 100644 --- a/src/platform/graphics/shader-utils.js +++ b/src/platform/graphics/shader-utils.js @@ -50,7 +50,7 @@ class ShaderUtils { * @param {string} [options.fragmentDefines] - The fragment shader defines. * @param {string} [options.fragmentExtensions] - The fragment shader extensions code. * @param {string} [options.fragmentPreamble] - The preamble string for the fragment shader. - * @param {boolean} [options.useTransformFeedback] - Whether to use transform feedback. Defaults to false + * @param {boolean} [options.useTransformFeedback] - Whether to use transform feedback. Defaults to false. * @param {string | string[]} [options.fragmentOutputTypes] - Fragment shader output types, which default to vec4. Passing a string will set the output type for all color attachments. Passing an array will set the output type for each color attachment. * @returns {object} Returns the created shader definition. */ From 37e062f873328ed38e853bc15433b35931489f11 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Tue, 16 Jan 2024 08:40:57 -0800 Subject: [PATCH 34/43] Additional PR comments --- src/platform/graphics/shader-utils.js | 15 +++++------ src/scene/graphics/render-pass-shader-quad.js | 23 +++++++++++++--- src/scene/shader-lib/utils.js | 27 +++++++++++++++++-- 3 files changed, 51 insertions(+), 14 deletions(-) diff --git a/src/platform/graphics/shader-utils.js b/src/platform/graphics/shader-utils.js index b3aaf5a7c0a..1d60f9c1d57 100644 --- a/src/platform/graphics/shader-utils.js +++ b/src/platform/graphics/shader-utils.js @@ -65,18 +65,17 @@ class ShaderUtils { // a define per supported color attachment, which strips out unsupported output definitions in the deviceIntro let attachmentsDefine = ''; - // Normalize fragmentOutputTypes to an array - let fragmentOutputTypes = options.fragmentOutputTypes ?? ['vec4']; - if (!Array.isArray(fragmentOutputTypes)) { - fragmentOutputTypes = [fragmentOutputTypes ?? 'vec4']; - } - // Define the fragment shader output type, vec4 by default if (!isVertex) { - let outType = ''; + // Normalize fragmentOutputTypes to an array + let fragmentOutputTypes = options.fragmentOutputTypes ?? 'vec4'; + if (!Array.isArray(fragmentOutputTypes)) { + fragmentOutputTypes = [fragmentOutputTypes]; + } + for (let i = 0; i < device.maxColorAttachments; i++) { attachmentsDefine += `#define COLOR_ATTACHMENT_${i}\n`; - outType = fragmentOutputTypes[i] ?? 'vec4'; + const outType = fragmentOutputTypes[i] ?? 'vec4'; attachmentsDefine += `#define outType_${i} ${outType}\n`; } } diff --git a/src/scene/graphics/render-pass-shader-quad.js b/src/scene/graphics/render-pass-shader-quad.js index ce92c3102fc..665cd007433 100644 --- a/src/scene/graphics/render-pass-shader-quad.js +++ b/src/scene/graphics/render-pass-shader-quad.js @@ -89,10 +89,25 @@ class RenderPassShaderQuad extends RenderPass { return this._shader; } - createQuadShader(name, fs, fragmentOutputType = 'vec4') { - return createShaderFromCode(this.device, RenderPassShaderQuad.quadVertexShader, fs, name, { - aPosition: SEMANTIC_POSITION - }, false, fragmentOutputType); + /** + * Creates a quad shader from the supplied fragment shader code. + * + * @param {string} name - A name of the shader. + * @param {string} fs - Fragment shader source code + * @param {object} [shaderDefinitionOptions] - Additional options that will be added to the shader definition. + * @param {boolean} [shaderDefinitionOptions.useTransformFeedback] - Whether to use transform feedback. Defaults to false. + * @param {string | string[]} [shaderDefinitionOptions.fragmentOutputTypes] - Fragment shader output types, which default to vec4. Passing a string will set the output type for all color attachments. Passing an array will set the output type for each color attachment. + * @returns {object} Returns the created shader definition. + */ + createQuadShader(name, fs, shaderDefinitionOptions = {}) { + return createShaderFromCode( + this.device, + RenderPassShaderQuad.quadVertexShader, + fs, + name, + { aPosition: SEMANTIC_POSITION }, + shaderDefinitionOptions + ); } destroy() { diff --git a/src/scene/shader-lib/utils.js b/src/scene/shader-lib/utils.js index 69203dbcee3..046f1360d93 100644 --- a/src/scene/shader-lib/utils.js +++ b/src/scene/shader-lib/utils.js @@ -12,13 +12,25 @@ import { ShaderGenerator } from './programs/shader-generator.js'; * graphics device. * @param {string} vsName - The vertex shader chunk name. * @param {string} fsName - The fragment shader chunk name. + * @param {boolean | Record} [useTransformFeedback] - Whether to use transform feedback. Defaults to false. * @param {object} [shaderDefinitionOptions] - Additional options that will be added to the shader definition. * @param {boolean} [shaderDefinitionOptions.useTransformFeedback] - Whether to use transform feedback. Defaults to false. * @param {string | string[]} [shaderDefinitionOptions.fragmentOutputTypes] - Fragment shader output types, which default to vec4. Passing a string will set the output type for all color attachments. Passing an array will set the output type for each color attachment. * @see ShaderUtils.createDefinition * @returns {Shader} The newly created shader. */ -function createShader(device, vsName, fsName, shaderDefinitionOptions) { +function createShader(device, vsName, fsName, useTransformFeedback = false, shaderDefinitionOptions = {}) { + + // Normalize arguments to allow passing shaderDefinitionOptions as the 6th argument + if (typeof useTransformFeedback === 'boolean') { + shaderDefinitionOptions.useTransformFeedback = useTransformFeedback; + } else if (typeof useTransformFeedback === 'object') { + shaderDefinitionOptions = { + ...shaderDefinitionOptions, + ...useTransformFeedback + }; + } + return new Shader(device, ShaderUtils.createDefinition(device, { ...shaderDefinitionOptions, name: `${vsName}_${fsName}`, @@ -42,17 +54,28 @@ function createShader(device, vsName, fsName, shaderDefinitionOptions) { * @param {Object} [attributes] - Object detailing the mapping of vertex shader * attribute names to semantics SEMANTIC_*. This enables the engine to match vertex buffer data as * inputs to the shader. Defaults to undefined, which generates the default attributes. + * @param {boolean | Record} [useTransformFeedback] - Whether to use transform feedback. Defaults to false. * @param {object} [shaderDefinitionOptions] - Additional options that will be added to the shader definition. * @param {boolean} [shaderDefinitionOptions.useTransformFeedback] - Whether to use transform feedback. Defaults to false. * @param {string | string[]} [shaderDefinitionOptions.fragmentOutputTypes] - Fragment shader output types, which default to vec4. Passing a string will set the output type for all color attachments. Passing an array will set the output type for each color attachment. * @see ShaderUtils.createDefinition * @returns {Shader} The newly created shader. */ -function createShaderFromCode(device, vsCode, fsCode, uniqueName, attributes, shaderDefinitionOptions) { +function createShaderFromCode(device, vsCode, fsCode, uniqueName, attributes, useTransformFeedback = false, shaderDefinitionOptions = {}) { // the function signature has changed, fail if called incorrectly Debug.assert(typeof attributes !== 'boolean'); + // Normalize arguments to allow passing shaderDefinitionOptions as the 6th argument + if (typeof useTransformFeedback === 'boolean') { + shaderDefinitionOptions.useTransformFeedback = useTransformFeedback; + } else if (typeof useTransformFeedback === 'object') { + shaderDefinitionOptions = { + ...shaderDefinitionOptions, + ...useTransformFeedback + }; + } + const programLibrary = getProgramLibrary(device); let shader = programLibrary.getCachedShader(uniqueName); if (!shader) { From 8df55e1c3e37e6bb4839de94eea0b48b43e04a9a Mon Sep 17 00:00:00 2001 From: Martin Valigursky <59932779+mvaligursky@users.noreply.github.com> Date: Wed, 17 Jan 2024 09:27:10 +0000 Subject: [PATCH 35/43] Update src/scene/graphics/render-pass-shader-quad.js --- src/scene/graphics/render-pass-shader-quad.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scene/graphics/render-pass-shader-quad.js b/src/scene/graphics/render-pass-shader-quad.js index 665cd007433..4809a79b10b 100644 --- a/src/scene/graphics/render-pass-shader-quad.js +++ b/src/scene/graphics/render-pass-shader-quad.js @@ -93,7 +93,7 @@ class RenderPassShaderQuad extends RenderPass { * Creates a quad shader from the supplied fragment shader code. * * @param {string} name - A name of the shader. - * @param {string} fs - Fragment shader source code + * @param {string} fs - Fragment shader source code. * @param {object} [shaderDefinitionOptions] - Additional options that will be added to the shader definition. * @param {boolean} [shaderDefinitionOptions.useTransformFeedback] - Whether to use transform feedback. Defaults to false. * @param {string | string[]} [shaderDefinitionOptions.fragmentOutputTypes] - Fragment shader output types, which default to vec4. Passing a string will set the output type for all color attachments. Passing an array will set the output type for each color attachment. From 4dd0110b6aac9ff3db87c2a0d8ea7ffa6d6c77ca Mon Sep 17 00:00:00 2001 From: Martin Valigursky <59932779+mvaligursky@users.noreply.github.com> Date: Wed, 17 Jan 2024 09:28:16 +0000 Subject: [PATCH 36/43] Update src/scene/graphics/render-pass-shader-quad.js --- src/scene/graphics/render-pass-shader-quad.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scene/graphics/render-pass-shader-quad.js b/src/scene/graphics/render-pass-shader-quad.js index 4809a79b10b..bc162dfef8f 100644 --- a/src/scene/graphics/render-pass-shader-quad.js +++ b/src/scene/graphics/render-pass-shader-quad.js @@ -97,7 +97,7 @@ class RenderPassShaderQuad extends RenderPass { * @param {object} [shaderDefinitionOptions] - Additional options that will be added to the shader definition. * @param {boolean} [shaderDefinitionOptions.useTransformFeedback] - Whether to use transform feedback. Defaults to false. * @param {string | string[]} [shaderDefinitionOptions.fragmentOutputTypes] - Fragment shader output types, which default to vec4. Passing a string will set the output type for all color attachments. Passing an array will set the output type for each color attachment. - * @returns {object} Returns the created shader definition. + * @returns {object} Returns the created shader. */ createQuadShader(name, fs, shaderDefinitionOptions = {}) { return createShaderFromCode( From 1f111c2e8e2ccca4dd5b25e62085906bab93ad92 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Wed, 17 Jan 2024 07:26:21 -0800 Subject: [PATCH 37/43] Add note explaining use of 'comparison' for integer textures --- src/platform/graphics/webgpu/webgpu-bind-group-format.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/platform/graphics/webgpu/webgpu-bind-group-format.js b/src/platform/graphics/webgpu/webgpu-bind-group-format.js index 31b3b330abe..0e4bec614bb 100644 --- a/src/platform/graphics/webgpu/webgpu-bind-group-format.js +++ b/src/platform/graphics/webgpu/webgpu-bind-group-format.js @@ -9,6 +9,13 @@ const samplerTypes = []; samplerTypes[SAMPLETYPE_FLOAT] = 'filtering'; samplerTypes[SAMPLETYPE_UNFILTERABLE_FLOAT] = 'non-filtering'; samplerTypes[SAMPLETYPE_DEPTH] = 'comparison'; + +// Using 'comparison' instead of 'non-filtering' may seem unusual, +// but currently we will get a validation error if we use 'non-filtering' +// along with texelFetch/textureLoad. 'comparison' works very well +// for the most common use-case of integer textures, texelFetch. +// We may be able to change how we initialize the sampler elsewhere +// to support 'non-filtering' in the future. samplerTypes[SAMPLETYPE_INT] = 'comparison'; samplerTypes[SAMPLETYPE_UINT] = 'comparison'; From a429aae9566918fe16deb0f502effab18aa934ec Mon Sep 17 00:00:00 2001 From: Liam Don Date: Wed, 17 Jan 2024 07:47:19 -0800 Subject: [PATCH 38/43] Update src/platform/graphics/webgpu/webgpu-bind-group-format.js Co-authored-by: Will Eastcott --- .../graphics/webgpu/webgpu-bind-group-format.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/platform/graphics/webgpu/webgpu-bind-group-format.js b/src/platform/graphics/webgpu/webgpu-bind-group-format.js index 0e4bec614bb..17446ceb53e 100644 --- a/src/platform/graphics/webgpu/webgpu-bind-group-format.js +++ b/src/platform/graphics/webgpu/webgpu-bind-group-format.js @@ -10,12 +10,10 @@ samplerTypes[SAMPLETYPE_FLOAT] = 'filtering'; samplerTypes[SAMPLETYPE_UNFILTERABLE_FLOAT] = 'non-filtering'; samplerTypes[SAMPLETYPE_DEPTH] = 'comparison'; -// Using 'comparison' instead of 'non-filtering' may seem unusual, -// but currently we will get a validation error if we use 'non-filtering' -// along with texelFetch/textureLoad. 'comparison' works very well -// for the most common use-case of integer textures, texelFetch. -// We may be able to change how we initialize the sampler elsewhere -// to support 'non-filtering' in the future. +// Using 'comparison' instead of 'non-filtering' may seem unusual, but currently we will get a +// validation error if we use 'non-filtering' along with texelFetch/textureLoad. 'comparison' works +// very well for the most common use-case of integer textures, texelFetch. We may be able to change +// how we initialize the sampler elsewhere to support 'non-filtering' in the future. samplerTypes[SAMPLETYPE_INT] = 'comparison'; samplerTypes[SAMPLETYPE_UINT] = 'comparison'; From 4f2e1b3ceacbc6d99e5ddff14be2dde774c71f3c Mon Sep 17 00:00:00 2001 From: Liam Don Date: Wed, 17 Jan 2024 07:48:06 -0800 Subject: [PATCH 39/43] Update src/platform/graphics/shader-utils.js Co-authored-by: Will Eastcott --- src/platform/graphics/shader-utils.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/platform/graphics/shader-utils.js b/src/platform/graphics/shader-utils.js index 1d60f9c1d57..0bdb0870236 100644 --- a/src/platform/graphics/shader-utils.js +++ b/src/platform/graphics/shader-utils.js @@ -51,7 +51,9 @@ class ShaderUtils { * @param {string} [options.fragmentExtensions] - The fragment shader extensions code. * @param {string} [options.fragmentPreamble] - The preamble string for the fragment shader. * @param {boolean} [options.useTransformFeedback] - Whether to use transform feedback. Defaults to false. - * @param {string | string[]} [options.fragmentOutputTypes] - Fragment shader output types, which default to vec4. Passing a string will set the output type for all color attachments. Passing an array will set the output type for each color attachment. + * @param {string | string[]} [options.fragmentOutputTypes] - Fragment shader output types, + * which default to vec4. Passing a string will set the output type for all color attachments. + * Passing an array will set the output type for each color attachment. * @returns {object} Returns the created shader definition. */ static createDefinition(device, options) { From a90f77d49544f08315cb86fa4101233ac73f1ed1 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Wed, 17 Jan 2024 07:49:22 -0800 Subject: [PATCH 40/43] Apply suggestions from code review Co-authored-by: Will Eastcott --- src/platform/graphics/shader.js | 4 +++- src/scene/shader-lib/utils.js | 26 ++++++++++++++++++-------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/platform/graphics/shader.js b/src/platform/graphics/shader.js index 6a122f6fe7f..db21985af5a 100644 --- a/src/platform/graphics/shader.js +++ b/src/platform/graphics/shader.js @@ -54,7 +54,9 @@ class Shader { * WebGPU platform. * @param {boolean} [definition.useTransformFeedback] - Specifies that this shader outputs * post-VS data to a buffer. - * @param {string | string[]} [definition.fragmentOutputTypes] - Fragment shader output types, which default to vec4. Passing a string will set the output type for all color attachments. Passing an array will set the output type for each color attachment. + * @param {string | string[]} [definition.fragmentOutputTypes] - Fragment shader output types, + * which default to vec4. Passing a string will set the output type for all color attachments. + * Passing an array will set the output type for each color attachment. * @param {string} [definition.shaderLanguage] - Specifies the shader language of vertex and * fragment shaders. Defaults to {@link SHADERLANGUAGE_GLSL}. * @example diff --git a/src/scene/shader-lib/utils.js b/src/scene/shader-lib/utils.js index 046f1360d93..b4be01c77e4 100644 --- a/src/scene/shader-lib/utils.js +++ b/src/scene/shader-lib/utils.js @@ -12,10 +12,15 @@ import { ShaderGenerator } from './programs/shader-generator.js'; * graphics device. * @param {string} vsName - The vertex shader chunk name. * @param {string} fsName - The fragment shader chunk name. - * @param {boolean | Record} [useTransformFeedback] - Whether to use transform feedback. Defaults to false. - * @param {object} [shaderDefinitionOptions] - Additional options that will be added to the shader definition. - * @param {boolean} [shaderDefinitionOptions.useTransformFeedback] - Whether to use transform feedback. Defaults to false. - * @param {string | string[]} [shaderDefinitionOptions.fragmentOutputTypes] - Fragment shader output types, which default to vec4. Passing a string will set the output type for all color attachments. Passing an array will set the output type for each color attachment. + * @param {boolean | Record} [useTransformFeedback] - Whether + * to use transform feedback. Defaults to false. + * @param {object} [shaderDefinitionOptions] - Additional options that will be added to the shader + * definition. + * @param {boolean} [shaderDefinitionOptions.useTransformFeedback] - Whether to use transform + * feedback. Defaults to false. + * @param {string | string[]} [shaderDefinitionOptions.fragmentOutputTypes] - Fragment shader + * output types, which default to vec4. Passing a string will set the output type for all color + * attachments. Passing an array will set the output type for each color attachment. * @see ShaderUtils.createDefinition * @returns {Shader} The newly created shader. */ @@ -54,10 +59,15 @@ function createShader(device, vsName, fsName, useTransformFeedback = false, shad * @param {Object} [attributes] - Object detailing the mapping of vertex shader * attribute names to semantics SEMANTIC_*. This enables the engine to match vertex buffer data as * inputs to the shader. Defaults to undefined, which generates the default attributes. - * @param {boolean | Record} [useTransformFeedback] - Whether to use transform feedback. Defaults to false. - * @param {object} [shaderDefinitionOptions] - Additional options that will be added to the shader definition. - * @param {boolean} [shaderDefinitionOptions.useTransformFeedback] - Whether to use transform feedback. Defaults to false. - * @param {string | string[]} [shaderDefinitionOptions.fragmentOutputTypes] - Fragment shader output types, which default to vec4. Passing a string will set the output type for all color attachments. Passing an array will set the output type for each color attachment. + * @param {boolean | Record} [useTransformFeedback] - Whether + * to use transform feedback. Defaults to false. + * @param {object} [shaderDefinitionOptions] - Additional options that will be added to the shader + * definition. + * @param {boolean} [shaderDefinitionOptions.useTransformFeedback] - Whether to use transform + * feedback. Defaults to false. + * @param {string | string[]} [shaderDefinitionOptions.fragmentOutputTypes] - Fragment shader + * output types, which default to vec4. Passing a string will set the output type for all color + * attachments. Passing an array will set the output type for each color attachment. * @see ShaderUtils.createDefinition * @returns {Shader} The newly created shader. */ From b401817dba352d371b48392770e997537ec87af5 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Wed, 17 Jan 2024 07:49:39 -0800 Subject: [PATCH 41/43] Update src/scene/graphics/render-pass-shader-quad.js Co-authored-by: Will Eastcott --- src/scene/graphics/render-pass-shader-quad.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/scene/graphics/render-pass-shader-quad.js b/src/scene/graphics/render-pass-shader-quad.js index bc162dfef8f..da78187ce02 100644 --- a/src/scene/graphics/render-pass-shader-quad.js +++ b/src/scene/graphics/render-pass-shader-quad.js @@ -94,9 +94,13 @@ class RenderPassShaderQuad extends RenderPass { * * @param {string} name - A name of the shader. * @param {string} fs - Fragment shader source code. - * @param {object} [shaderDefinitionOptions] - Additional options that will be added to the shader definition. - * @param {boolean} [shaderDefinitionOptions.useTransformFeedback] - Whether to use transform feedback. Defaults to false. - * @param {string | string[]} [shaderDefinitionOptions.fragmentOutputTypes] - Fragment shader output types, which default to vec4. Passing a string will set the output type for all color attachments. Passing an array will set the output type for each color attachment. + * @param {object} [shaderDefinitionOptions] - Additional options that will be added to the + * shader definition. + * @param {boolean} [shaderDefinitionOptions.useTransformFeedback] - Whether to use transform + * feedback. Defaults to false. + * @param {string | string[]} [shaderDefinitionOptions.fragmentOutputTypes] - Fragment shader + * output types, which default to vec4. Passing a string will set the output type for all color + * attachments. Passing an array will set the output type for each color attachment. * @returns {object} Returns the created shader. */ createQuadShader(name, fs, shaderDefinitionOptions = {}) { From a436a9f6f5387896c93c54e42aae9868367165a3 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Thu, 18 Jan 2024 07:29:25 -0800 Subject: [PATCH 42/43] For unsigned pixel formats, use U instead of UI --- .../examples/graphics/integer-textures.mjs | 2 +- src/platform/graphics/constants.js | 48 +++++++++---------- src/platform/graphics/webgl/webgl-texture.js | 26 +++++----- src/platform/graphics/webgpu/constants.js | 26 +++++----- 4 files changed, 51 insertions(+), 51 deletions(-) diff --git a/examples/src/examples/graphics/integer-textures.mjs b/examples/src/examples/graphics/integer-textures.mjs index e463f4b432f..01e92d13ae1 100644 --- a/examples/src/examples/graphics/integer-textures.mjs +++ b/examples/src/examples/graphics/integer-textures.mjs @@ -126,7 +126,7 @@ async function example({ canvas, data, deviceType, files, glslangPath, twgslPath // This can be helpful for storing bitfields in each pixel. // In this example, we are storing 3 different properties // in a single Uint8 value. - format: pc.PIXELFORMAT_R8UI, + format: pc.PIXELFORMAT_R8U, addressU: pc.ADDRESS_CLAMP_TO_EDGE, addressV: pc.ADDRESS_CLAMP_TO_EDGE }); diff --git a/src/platform/graphics/constants.js b/src/platform/graphics/constants.js index 1813a591f38..ed006195ddb 100644 --- a/src/platform/graphics/constants.js +++ b/src/platform/graphics/constants.js @@ -630,7 +630,7 @@ export const PIXELFORMAT_R8I = 32; * * @type {number} */ -export const PIXELFORMAT_R8UI = 33; +export const PIXELFORMAT_R8U = 33; /** * 16-bit signed integer single-channel (R) format (Not supported by WebGL1). @@ -644,7 +644,7 @@ export const PIXELFORMAT_R16I = 34; * * @type {number} */ -export const PIXELFORMAT_R16UI = 35; +export const PIXELFORMAT_R16U = 35; /** * 32-bit signed integer single-channel (R) format (Not supported by WebGL1). @@ -658,7 +658,7 @@ export const PIXELFORMAT_R32I = 36; * * @type {number} */ -export const PIXELFORMAT_R32UI = 37; +export const PIXELFORMAT_R32U = 37; /** * 8-bit per-channel signed integer (RG) format (Not supported by WebGL1). @@ -672,7 +672,7 @@ export const PIXELFORMAT_RG8I = 38; * * @type {number} */ -export const PIXELFORMAT_RG8UI = 39; +export const PIXELFORMAT_RG8U = 39; /** * 16-bit per-channel signed integer (RG) format (Not supported by WebGL1). @@ -686,7 +686,7 @@ export const PIXELFORMAT_RG16I = 40; * * @type {number} */ -export const PIXELFORMAT_RG16UI = 41; +export const PIXELFORMAT_RG16U = 41; /** * 32-bit per-channel signed integer (RG) format (Not supported by WebGL1). @@ -700,7 +700,7 @@ export const PIXELFORMAT_RG32I = 42; * * @type {number} */ -export const PIXELFORMAT_RG32UI = 43; +export const PIXELFORMAT_RG32U = 43; /** * 8-bit per-channel signed integer (RGBA) format (Not supported by WebGL1). @@ -714,7 +714,7 @@ export const PIXELFORMAT_RGBA8I = 44; * * @type {number} */ -export const PIXELFORMAT_RGBA8UI = 45; +export const PIXELFORMAT_RGBA8U = 45; /** * 16-bit per-channel signed integer (RGBA) format (Not supported by WebGL1). @@ -728,7 +728,7 @@ export const PIXELFORMAT_RGBA16I = 46; * * @type {number} */ -export const PIXELFORMAT_RGBA16UI = 47; +export const PIXELFORMAT_RGBA16U = 47; /** * 32-bit per-channel signed integer (RGBA) format (Not supported by WebGL1). @@ -742,7 +742,7 @@ export const PIXELFORMAT_RGBA32I = 48; * * @type {number} */ -export const PIXELFORMAT_RGBA32UI = 49; +export const PIXELFORMAT_RGBA32U = 49; // map of engine PIXELFORMAT_*** enums to information about the format @@ -786,23 +786,23 @@ export const pixelFormatInfo = new Map([ // uncompressed integer formats (Not supported on WebGL1) [PIXELFORMAT_R8I, { name: 'R8I', size: 1, isInt: true }], - [PIXELFORMAT_R8UI, { name: 'R8UI', size: 1, isInt: true }], + [PIXELFORMAT_R8U, { name: 'R8U', size: 1, isInt: true }], [PIXELFORMAT_R16I, { name: 'R16I', size: 2, isInt: true }], - [PIXELFORMAT_R16UI, { name: 'R16UI', size: 2, isInt: true }], + [PIXELFORMAT_R16U, { name: 'R16UI', size: 2, isInt: true }], [PIXELFORMAT_R32I, { name: 'R32I', size: 4, isInt: true }], - [PIXELFORMAT_R32UI, { name: 'R32UI', size: 4, isInt: true }], + [PIXELFORMAT_R32U, { name: 'R32UI', size: 4, isInt: true }], [PIXELFORMAT_RG8I, { name: 'RG8I', size: 2, isInt: true }], - [PIXELFORMAT_RG8UI, { name: 'RG8UI', size: 2, isInt: true }], + [PIXELFORMAT_RG8U, { name: 'RG8UI', size: 2, isInt: true }], [PIXELFORMAT_RG16I, { name: 'RG16I', size: 4, isInt: true }], - [PIXELFORMAT_RG16UI, { name: 'RG16UI', size: 4, isInt: true }], + [PIXELFORMAT_RG16U, { name: 'RG16UI', size: 4, isInt: true }], [PIXELFORMAT_RG32I, { name: 'RG32I', size: 8, isInt: true }], - [PIXELFORMAT_RG32UI, { name: 'RG32UI', size: 8, isInt: true }], + [PIXELFORMAT_RG32U, { name: 'RG32UI', size: 8, isInt: true }], [PIXELFORMAT_RGBA8I, { name: 'RGBA8I', size: 4, isInt: true }], - [PIXELFORMAT_RGBA8UI, { name: 'RGBA8UI', size: 4, isInt: true }], + [PIXELFORMAT_RGBA8U, { name: 'RGBA8UI', size: 4, isInt: true }], [PIXELFORMAT_RGBA16I, { name: 'RGBA16I', size: 8, isInt: true }], - [PIXELFORMAT_RGBA16UI, { name: 'RGBA16UI', size: 8, isInt: true }], + [PIXELFORMAT_RGBA16U, { name: 'RGBA16UI', size: 8, isInt: true }], [PIXELFORMAT_RGBA32I, { name: 'RGBA32I', size: 16, isInt: true }], - [PIXELFORMAT_RGBA32UI, { name: 'RGBA32UI', size: 16, isInt: true }] + [PIXELFORMAT_RGBA32U, { name: 'RGBA32UI', size: 16, isInt: true }] ]); // update this function when exposing additional compressed pixel formats @@ -824,17 +824,17 @@ export const getPixelFormatArrayType = (format) => { case PIXELFORMAT_RG32I: case PIXELFORMAT_RGBA32I: return Int32Array; - case PIXELFORMAT_R32UI: - case PIXELFORMAT_RG32UI: - case PIXELFORMAT_RGBA32UI: + case PIXELFORMAT_R32U: + case PIXELFORMAT_RG32U: + case PIXELFORMAT_RGBA32U: return Uint32Array; case PIXELFORMAT_R16I: case PIXELFORMAT_RG16I: case PIXELFORMAT_RGBA16I: return Int16Array; - case PIXELFORMAT_R16UI: - case PIXELFORMAT_RG16UI: - case PIXELFORMAT_RGBA16UI: + case PIXELFORMAT_R16U: + case PIXELFORMAT_RG16U: + case PIXELFORMAT_RGBA16U: case PIXELFORMAT_RGB565: case PIXELFORMAT_RGBA5551: case PIXELFORMAT_RGBA4: diff --git a/src/platform/graphics/webgl/webgl-texture.js b/src/platform/graphics/webgl/webgl-texture.js index 3f30126d18b..c6599e0d419 100644 --- a/src/platform/graphics/webgl/webgl-texture.js +++ b/src/platform/graphics/webgl/webgl-texture.js @@ -7,10 +7,10 @@ import { PIXELFORMAT_DEPTHSTENCIL, PIXELFORMAT_111110F, PIXELFORMAT_SRGB, PIXELFORMAT_SRGBA, PIXELFORMAT_ETC1, PIXELFORMAT_ETC2_RGB, PIXELFORMAT_ETC2_RGBA, PIXELFORMAT_PVRTC_2BPP_RGB_1, PIXELFORMAT_PVRTC_2BPP_RGBA_1, PIXELFORMAT_PVRTC_4BPP_RGB_1, PIXELFORMAT_PVRTC_4BPP_RGBA_1, PIXELFORMAT_ASTC_4x4, PIXELFORMAT_ATC_RGB, - PIXELFORMAT_ATC_RGBA, PIXELFORMAT_BGRA8, PIXELFORMAT_R8I, PIXELFORMAT_R8UI, PIXELFORMAT_R16I, PIXELFORMAT_R16UI, - PIXELFORMAT_R32I, PIXELFORMAT_R32UI, PIXELFORMAT_RG16I, PIXELFORMAT_RG16UI, PIXELFORMAT_RG32I, PIXELFORMAT_RG32UI, - PIXELFORMAT_RG8I, PIXELFORMAT_RG8UI, PIXELFORMAT_RGBA16I, PIXELFORMAT_RGBA16UI, PIXELFORMAT_RGBA32I, PIXELFORMAT_RGBA32UI, - PIXELFORMAT_RGBA8I, PIXELFORMAT_RGBA8UI + PIXELFORMAT_ATC_RGBA, PIXELFORMAT_BGRA8, PIXELFORMAT_R8I, PIXELFORMAT_R8U, PIXELFORMAT_R16I, PIXELFORMAT_R16U, + PIXELFORMAT_R32I, PIXELFORMAT_R32U, PIXELFORMAT_RG16I, PIXELFORMAT_RG16U, PIXELFORMAT_RG32I, PIXELFORMAT_RG32U, + PIXELFORMAT_RG8I, PIXELFORMAT_RG8U, PIXELFORMAT_RGBA16I, PIXELFORMAT_RGBA16U, PIXELFORMAT_RGBA32I, PIXELFORMAT_RGBA32U, + PIXELFORMAT_RGBA8I, PIXELFORMAT_RGBA8U } from '../constants.js'; /** @@ -288,7 +288,7 @@ class WebglTexture { this._glInternalFormat = gl.R8I; this._glPixelType = gl.BYTE; break; - case PIXELFORMAT_R8UI: // WebGL2 only + case PIXELFORMAT_R8U: // WebGL2 only this._glFormat = gl.RED_INTEGER; this._glInternalFormat = gl.R8UI; this._glPixelType = gl.UNSIGNED_BYTE; @@ -298,7 +298,7 @@ class WebglTexture { this._glInternalFormat = gl.R16I; this._glPixelType = gl.SHORT; break; - case PIXELFORMAT_R16UI: // WebGL2 only + case PIXELFORMAT_R16U: // WebGL2 only this._glFormat = gl.RED_INTEGER; this._glInternalFormat = gl.R16UI; this._glPixelType = gl.UNSIGNED_SHORT; @@ -308,7 +308,7 @@ class WebglTexture { this._glInternalFormat = gl.R32I; this._glPixelType = gl.INT; break; - case PIXELFORMAT_R32UI: // WebGL2 only + case PIXELFORMAT_R32U: // WebGL2 only this._glFormat = gl.RED_INTEGER; this._glInternalFormat = gl.R32UI; this._glPixelType = gl.UNSIGNED_INT; @@ -319,7 +319,7 @@ class WebglTexture { this._glInternalFormat = gl.RG8I; this._glPixelType = gl.BYTE; break; - case PIXELFORMAT_RG8UI: // WebGL2 only + case PIXELFORMAT_RG8U: // WebGL2 only this._glFormat = gl.RG_INTEGER; this._glInternalFormat = gl.RG8UI; this._glPixelType = gl.UNSIGNED_BYTE; @@ -329,7 +329,7 @@ class WebglTexture { this._glInternalFormat = gl.RG16I; this._glPixelType = gl.SHORT; break; - case PIXELFORMAT_RG16UI: // WebGL2 only + case PIXELFORMAT_RG16U: // WebGL2 only this._glFormat = gl.RG_INTEGER; this._glInternalFormat = gl.RG16UI; this._glPixelType = gl.UNSIGNED_SHORT; @@ -339,7 +339,7 @@ class WebglTexture { this._glInternalFormat = gl.RG32I; this._glPixelType = gl.INT; break; - case PIXELFORMAT_RG32UI: // WebGL2 only + case PIXELFORMAT_RG32U: // WebGL2 only this._glFormat = gl.RG_INTEGER; this._glInternalFormat = gl.RG32UI; this._glPixelType = gl.UNSIGNED_INT; @@ -350,7 +350,7 @@ class WebglTexture { this._glInternalFormat = gl.RGBA8I; this._glPixelType = gl.BYTE; break; - case PIXELFORMAT_RGBA8UI: // WebGL2 only + case PIXELFORMAT_RGBA8U: // WebGL2 only this._glFormat = gl.RGBA_INTEGER; this._glInternalFormat = gl.RGBA8UI; this._glPixelType = gl.UNSIGNED_BYTE; @@ -360,7 +360,7 @@ class WebglTexture { this._glInternalFormat = gl.RGBA16I; this._glPixelType = gl.SHORT; break; - case PIXELFORMAT_RGBA16UI: // WebGL2 only + case PIXELFORMAT_RGBA16U: // WebGL2 only this._glFormat = gl.RGBA_INTEGER; this._glInternalFormat = gl.RGBA16UI; this._glPixelType = gl.UNSIGNED_SHORT; @@ -370,7 +370,7 @@ class WebglTexture { this._glInternalFormat = gl.RGBA32I; this._glPixelType = gl.INT; break; - case PIXELFORMAT_RGBA32UI: // WebGL2 only + case PIXELFORMAT_RGBA32U: // WebGL2 only this._glFormat = gl.RGBA_INTEGER; this._glInternalFormat = gl.RGBA32UI; this._glPixelType = gl.UNSIGNED_INT; diff --git a/src/platform/graphics/webgpu/constants.js b/src/platform/graphics/webgpu/constants.js index ec5e4e5da08..da2299e51ec 100644 --- a/src/platform/graphics/webgpu/constants.js +++ b/src/platform/graphics/webgpu/constants.js @@ -5,10 +5,10 @@ import { PIXELFORMAT_DEPTHSTENCIL, PIXELFORMAT_111110F, PIXELFORMAT_SRGB, PIXELFORMAT_SRGBA, PIXELFORMAT_ETC1, PIXELFORMAT_ETC2_RGB, PIXELFORMAT_ETC2_RGBA, PIXELFORMAT_PVRTC_2BPP_RGB_1, PIXELFORMAT_PVRTC_2BPP_RGBA_1, PIXELFORMAT_PVRTC_4BPP_RGB_1, PIXELFORMAT_PVRTC_4BPP_RGBA_1, PIXELFORMAT_ASTC_4x4, PIXELFORMAT_ATC_RGB, - PIXELFORMAT_ATC_RGBA, PIXELFORMAT_BGRA8, PIXELFORMAT_R8I, PIXELFORMAT_R8UI, PIXELFORMAT_R16I, PIXELFORMAT_R16UI, - PIXELFORMAT_R32I, PIXELFORMAT_R32UI, PIXELFORMAT_RG16I, PIXELFORMAT_RG16UI, PIXELFORMAT_RG32I, PIXELFORMAT_RG32UI, - PIXELFORMAT_RG8I, PIXELFORMAT_RG8UI, PIXELFORMAT_RGBA16I, PIXELFORMAT_RGBA16UI, PIXELFORMAT_RGBA32I, PIXELFORMAT_RGBA32UI, - PIXELFORMAT_RGBA8I, PIXELFORMAT_RGBA8UI + PIXELFORMAT_ATC_RGBA, PIXELFORMAT_BGRA8, PIXELFORMAT_R8I, PIXELFORMAT_R8U, PIXELFORMAT_R16I, PIXELFORMAT_R16U, + PIXELFORMAT_R32I, PIXELFORMAT_R32U, PIXELFORMAT_RG16I, PIXELFORMAT_RG16U, PIXELFORMAT_RG32I, PIXELFORMAT_RG32U, + PIXELFORMAT_RG8I, PIXELFORMAT_RG8U, PIXELFORMAT_RGBA16I, PIXELFORMAT_RGBA16U, PIXELFORMAT_RGBA32I, PIXELFORMAT_RGBA32U, + PIXELFORMAT_RGBA8I, PIXELFORMAT_RGBA8U } from '../constants.js'; // map of PIXELFORMAT_*** to GPUTextureFormat @@ -46,20 +46,20 @@ gpuTextureFormats[PIXELFORMAT_ATC_RGB] = ''; gpuTextureFormats[PIXELFORMAT_ATC_RGBA] = ''; gpuTextureFormats[PIXELFORMAT_BGRA8] = 'bgra8unorm'; gpuTextureFormats[PIXELFORMAT_R8I] = 'r8sint'; -gpuTextureFormats[PIXELFORMAT_R8UI] = 'r8uint'; +gpuTextureFormats[PIXELFORMAT_R8U] = 'r8uint'; gpuTextureFormats[PIXELFORMAT_R16I] = 'r16sint'; -gpuTextureFormats[PIXELFORMAT_R16UI] = 'r16uint'; +gpuTextureFormats[PIXELFORMAT_R16U] = 'r16uint'; gpuTextureFormats[PIXELFORMAT_R32I] = 'r32sint'; -gpuTextureFormats[PIXELFORMAT_R32UI] = 'r32uint'; +gpuTextureFormats[PIXELFORMAT_R32U] = 'r32uint'; gpuTextureFormats[PIXELFORMAT_RG8I] = 'rg8sint'; -gpuTextureFormats[PIXELFORMAT_RG8UI] = 'rg8uint'; +gpuTextureFormats[PIXELFORMAT_RG8U] = 'rg8uint'; gpuTextureFormats[PIXELFORMAT_RG16I] = 'rg16sint'; -gpuTextureFormats[PIXELFORMAT_RG16UI] = 'rg16uint'; +gpuTextureFormats[PIXELFORMAT_RG16U] = 'rg16uint'; gpuTextureFormats[PIXELFORMAT_RG32I] = 'rg32sint'; -gpuTextureFormats[PIXELFORMAT_RG32UI] = 'rg32uint'; +gpuTextureFormats[PIXELFORMAT_RG32U] = 'rg32uint'; gpuTextureFormats[PIXELFORMAT_RGBA8I] = 'rgba8sint'; -gpuTextureFormats[PIXELFORMAT_RGBA8UI] = 'rgba8uint'; +gpuTextureFormats[PIXELFORMAT_RGBA8U] = 'rgba8uint'; gpuTextureFormats[PIXELFORMAT_RGBA16I] = 'rgba16sint'; -gpuTextureFormats[PIXELFORMAT_RGBA16UI] = 'rgba16uint'; +gpuTextureFormats[PIXELFORMAT_RGBA16U] = 'rgba16uint'; gpuTextureFormats[PIXELFORMAT_RGBA32I] = 'rgba32sint'; -gpuTextureFormats[PIXELFORMAT_RGBA32UI] = 'rgba32uint'; +gpuTextureFormats[PIXELFORMAT_RGBA32U] = 'rgba32uint'; From 9c6a482b2927435d2afbb3653849a524661d4080 Mon Sep 17 00:00:00 2001 From: Liam Don Date: Thu, 18 Jan 2024 07:41:25 -0800 Subject: [PATCH 43/43] Change string names of pixelFormatInfo --- src/platform/graphics/constants.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/platform/graphics/constants.js b/src/platform/graphics/constants.js index ed006195ddb..712b1c0102a 100644 --- a/src/platform/graphics/constants.js +++ b/src/platform/graphics/constants.js @@ -786,23 +786,23 @@ export const pixelFormatInfo = new Map([ // uncompressed integer formats (Not supported on WebGL1) [PIXELFORMAT_R8I, { name: 'R8I', size: 1, isInt: true }], - [PIXELFORMAT_R8U, { name: 'R8U', size: 1, isInt: true }], + [PIXELFORMAT_R8U, { name: 'R8U', size: 1, isInt: true }], [PIXELFORMAT_R16I, { name: 'R16I', size: 2, isInt: true }], - [PIXELFORMAT_R16U, { name: 'R16UI', size: 2, isInt: true }], + [PIXELFORMAT_R16U, { name: 'R16U', size: 2, isInt: true }], [PIXELFORMAT_R32I, { name: 'R32I', size: 4, isInt: true }], - [PIXELFORMAT_R32U, { name: 'R32UI', size: 4, isInt: true }], + [PIXELFORMAT_R32U, { name: 'R32U', size: 4, isInt: true }], [PIXELFORMAT_RG8I, { name: 'RG8I', size: 2, isInt: true }], - [PIXELFORMAT_RG8U, { name: 'RG8UI', size: 2, isInt: true }], + [PIXELFORMAT_RG8U, { name: 'RG8U', size: 2, isInt: true }], [PIXELFORMAT_RG16I, { name: 'RG16I', size: 4, isInt: true }], - [PIXELFORMAT_RG16U, { name: 'RG16UI', size: 4, isInt: true }], + [PIXELFORMAT_RG16U, { name: 'RG16U', size: 4, isInt: true }], [PIXELFORMAT_RG32I, { name: 'RG32I', size: 8, isInt: true }], - [PIXELFORMAT_RG32U, { name: 'RG32UI', size: 8, isInt: true }], + [PIXELFORMAT_RG32U, { name: 'RG32U', size: 8, isInt: true }], [PIXELFORMAT_RGBA8I, { name: 'RGBA8I', size: 4, isInt: true }], - [PIXELFORMAT_RGBA8U, { name: 'RGBA8UI', size: 4, isInt: true }], + [PIXELFORMAT_RGBA8U, { name: 'RGBA8U', size: 4, isInt: true }], [PIXELFORMAT_RGBA16I, { name: 'RGBA16I', size: 8, isInt: true }], - [PIXELFORMAT_RGBA16U, { name: 'RGBA16UI', size: 8, isInt: true }], + [PIXELFORMAT_RGBA16U, { name: 'RGBA16U', size: 8, isInt: true }], [PIXELFORMAT_RGBA32I, { name: 'RGBA32I', size: 16, isInt: true }], - [PIXELFORMAT_RGBA32U, { name: 'RGBA32UI', size: 16, isInt: true }] + [PIXELFORMAT_RGBA32U, { name: 'RGBA32U', size: 16, isInt: true }] ]); // update this function when exposing additional compressed pixel formats