diff --git a/3rdparty/webgpu-utils-1.x.module.js b/3rdparty/webgpu-utils-1.x.module.js index d9d79b99..e7b80243 100644 --- a/3rdparty/webgpu-utils-1.x.module.js +++ b/3rdparty/webgpu-utils-1.x.module.js @@ -1,4 +1,4 @@ -/* webgpu-utils@1.7.1, license MIT */ +/* webgpu-utils@1.9.1, license MIT */ const roundUpToMultipleOf = (v, multiple) => (((v + multiple - 1) / multiple) | 0) * multiple; function keysOf(obj) { return Object.keys(obj); @@ -32,7 +32,8 @@ function subarray(arr, offset, length) { // TODO: fix better? const isTypedArray = (arr) => arr && typeof arr.length === 'number' && arr.buffer instanceof ArrayBuffer && typeof arr.byteLength === 'number'; -const b = { +const createTypeDefs = (defs) => defs; +const b = createTypeDefs({ i32: { numElements: 1, align: 4, size: 4, type: 'i32', View: Int32Array }, u32: { numElements: 1, align: 4, size: 4, type: 'u32', View: Uint32Array }, f32: { numElements: 1, align: 4, size: 4, type: 'f32', View: Float32Array }, @@ -72,8 +73,8 @@ const b = { // You can only create one in an internal struct. But, this code generates // views of structs and it needs to not fail if the struct has a bool bool: { numElements: 0, align: 1, size: 0, type: 'bool', View: Uint32Array }, -}; -const typeInfo = { +}); +const kWGSLTypeInfo = createTypeDefs({ ...b, 'atomic': b.i32, 'atomic': b.u32, @@ -107,8 +108,9 @@ const typeInfo = { 'mat3x4': b.mat3x4h, 'mat4x4': b.mat4x4f, 'mat4x4': b.mat4x4h, -}; -const kTypes = keysOf(typeInfo); +}); +const kWGSLTypes = keysOf(kWGSLTypeInfo); + /** * Set which intrinsic types to make views for. * @@ -122,7 +124,7 @@ const kTypes = keysOf(typeInfo); * what you want. * * If you do want individual views then you'd call - * `setIntrinsicsToView(['vec3f`])` and now you get + * `setIntrinsicsToView(['vec3f'])` and now you get * an array of 200 `Float32Array`s. * * Note: `setIntrinsicsToView` always sets ALL types. The list you @@ -130,8 +132,8 @@ const kTypes = keysOf(typeInfo); * will be reset to do the default. In other words * * ```js - * setIntrinsicsToView(['vec3f`]) - * setIntrinsicsToView(['vec2f`]) + * setIntrinsicsToView(['vec3f']) + * setIntrinsicsToView(['vec2f']) * ``` * * Only `vec2f` will have views created. `vec3f` has been reset to the default by @@ -151,8 +153,8 @@ function setIntrinsicsToView(types = [], flatten) { // we need to track what we've viewed because for example `vec3f` references // the same info as `vec3` so we'd set one and reset the other. const visited = new Set(); - for (const type of kTypes) { - const info = typeInfo[type]; + for (const type of kWGSLTypes) { + const info = kWGSLTypeInfo[type]; if (!visited.has(info)) { visited.add(info); info.flatten = types.includes(type) ? flatten : !flatten; @@ -184,7 +186,7 @@ function getSizeOfTypeDef(typeDef) { } else { const asIntrinsicDef = typeDef; - const { align } = typeInfo[asIntrinsicDef.type]; + const { align } = kWGSLTypeInfo[asIntrinsicDef.type]; return numElements > 1 ? roundUpToMultipleOf(typeDef.size, align) * numElements : typeDef.size; @@ -199,7 +201,7 @@ function getSizeOfTypeDef(typeDef) { function makeIntrinsicTypedArrayView(typeDef, buffer, baseOffset, numElements) { const { size, type } = typeDef; try { - const { View, align } = typeInfo[type]; + const { View, align } = kWGSLTypeInfo[type]; const isArray = numElements !== undefined; const sizeInBytes = isArray ? roundUpToMultipleOf(size, align) @@ -270,7 +272,7 @@ function makeTypedArrayViews(typeDef, arrayBuffer, offset) { // On the other hand, if we have `array` the maybe we do want // 10 `Float32Array(16)` views since you might want to do // `mat4.perspective(fov, aspect, near, far, foo.bar.arrayOf10Mat4s[3])`; - if (isIntrinsic(elementType) && typeInfo[elementType.type].flatten) { + if (isIntrinsic(elementType) && kWGSLTypeInfo[elementType.type].flatten) { return makeIntrinsicTypedArrayView(elementType, buffer, baseOffset, asArrayDef.numElements); } else { @@ -422,7 +424,7 @@ function isArrayLikeOfNumber(data) { } function setIntrinsicFromArrayLikeOfNumber(typeDef, data, arrayBuffer, offset) { const asIntrinsicDefinition = typeDef; - const type = typeInfo[asIntrinsicDefinition.type]; + const type = kWGSLTypeInfo[asIntrinsicDefinition.type]; const view = getView(arrayBuffer, type.View); const index = offset / view.BYTES_PER_ELEMENT; if (typeof data === 'number') { @@ -494,7 +496,7 @@ function getAlignmentOfTypeDef(typeDef) { return Object.values(fields).reduce((max, { type }) => Math.max(max, getAlignmentOfTypeDef(type)), 0); } const { type } = typeDef; - const { align } = typeInfo[type]; + const { align } = kWGSLTypeInfo[type]; return align; } function getSizeAndAlignmentOfUnsizedArrayElementOfTypeDef(typeDef) { @@ -1008,6 +1010,20 @@ class Enable extends Statement { return "enable"; } } +/** + * @class Requires + * @extends Statement + * @category AST + */ +class Requires extends Statement { + constructor(extensions) { + super(); + this.extensions = extensions; + } + get astNodeType() { + return "requires"; + } +} /** * @class Diagnostic * @extends Statement @@ -1381,6 +1397,9 @@ class VariableExpr extends Expression { } search(callback) { callback(this); + if (this.postfix) { + this.postfix.search(callback); + } } evaluate(context) { const constant = context.constants.get(this.name); @@ -1500,6 +1519,20 @@ class GroupingExpr extends Expression { this.searchBlock(this.contents, callback); } } +/** + * @class ArrayIndex + * @extends Expression + * @category AST + */ +class ArrayIndex extends Expression { + constructor(index) { + super(); + this.index = index; + } + search(callback) { + this.index.search(callback); + } +} /** * @class Operator * @extends Expression @@ -1817,10 +1850,10 @@ TokenTypes.keywords = { continue: new TokenType("continue", TokenClass.keyword, "continue"), continuing: new TokenType("continuing", TokenClass.keyword, "continuing"), default: new TokenType("default", TokenClass.keyword, "default"), + diagnostic: new TokenType("diagnostic", TokenClass.keyword, "diagnostic"), discard: new TokenType("discard", TokenClass.keyword, "discard"), else: new TokenType("else", TokenClass.keyword, "else"), enable: new TokenType("enable", TokenClass.keyword, "enable"), - diagnostic: new TokenType("diagnostic", TokenClass.keyword, "diagnostic"), fallthrough: new TokenType("fallthrough", TokenClass.keyword, "fallthrough"), false: new TokenType("false", TokenClass.keyword, "false"), fn: new TokenType("fn", TokenClass.keyword, "fn"), @@ -1835,6 +1868,7 @@ TokenTypes.keywords = { read: new TokenType("read", TokenClass.keyword, "read"), read_write: new TokenType("read_write", TokenClass.keyword, "read_write"), return: new TokenType("return", TokenClass.keyword, "return"), + requires: new TokenType("requires", TokenClass.keyword, "requires"), storage: new TokenType("storage", TokenClass.keyword, "storage"), switch: new TokenType("switch", TokenClass.keyword, "switch"), true: new TokenType("true", TokenClass.keyword, "true"), @@ -2543,6 +2577,11 @@ class WgslParser { this._consume(TokenTypes.tokens.semicolon, "Expected ';'"); return directive; } + if (this._match(TokenTypes.keywords.requires)) { + const requires = this._requires_directive(); + this._consume(TokenTypes.tokens.semicolon, "Expected ';'"); + return requires; + } if (this._match(TokenTypes.keywords.enable)) { const enable = this._enable_directive(); this._consume(TokenTypes.tokens.semicolon, "Expected ';'"); @@ -3177,8 +3216,9 @@ class WgslParser { // primary_expression postfix_expression ? const expr = this._primary_expression(); const p = this._postfix_expression(); - if (p) + if (p) { expr.postfix = p; + } return expr; } _postfix_expression() { @@ -3186,18 +3226,21 @@ class WgslParser { if (this._match(TokenTypes.tokens.bracket_left)) { const expr = this._short_circuit_or_expression(); this._consume(TokenTypes.tokens.bracket_right, "Expected ']'."); + const arrayIndex = new ArrayIndex(expr); const p = this._postfix_expression(); - if (p) - expr.postfix = p; - return expr; + if (p) { + arrayIndex.postfix = p; + } + return arrayIndex; } // period ident postfix_expression? if (this._match(TokenTypes.tokens.period)) { const name = this._consume(TokenTypes.tokens.ident, "Expected member name."); const p = this._postfix_expression(); const expr = new StringExpr(name.lexeme); - if (p) + if (p) { expr.postfix = p; + } return expr; } return null; @@ -3462,6 +3505,15 @@ class WgslParser { const name = this._consume(TokenTypes.tokens.ident, "identity expected."); return new Enable(name.toString()); } + _requires_directive() { + // requires extension [, extension]* semicolon + const extensions = [this._consume(TokenTypes.tokens.ident, "identity expected.").toString()]; + while (this._match(TokenTypes.tokens.comma)) { + const name = this._consume(TokenTypes.tokens.ident, "identity expected."); + extensions.push(name.toString()); + } + return new Requires(extensions); + } _type_alias() { // type ident equal type_decl const name = this._consume(TokenTypes.tokens.ident, "identity expected."); @@ -4741,7 +4793,9 @@ function getBindGroupLayoutEntry(resource, visibility) { return { binding, visibility, - buffer: {}, + buffer: { + ...(resource.size && { minBindingSize: resource.size }), + }, }; case ResourceType.Storage: return { @@ -4749,6 +4803,7 @@ function getBindGroupLayoutEntry(resource, visibility) { visibility, buffer: { type: (access === '' || access === 'read') ? 'read-only-storage' : 'storage', + ...(resource.size && { minBindingSize: resource.size }), }, }; case ResourceType.Texture: { @@ -4961,7 +5016,7 @@ function addType(reflect, typeInfo, offset) { // IntrinsicDefinition return { size: typeInfo.size, - type, + type: type, }; } } @@ -5728,6 +5783,7 @@ function uploadDataToTexture(device, texture, source, options) { * to a texture and then optionally generates mip levels */ function copySourcesToTexture(device, texture, sources, options = {}) { + let tempTexture; sources.forEach((source, layer) => { const origin = [0, 0, layer + (options.baseArrayLayer || 0)]; if (isTextureRawDataSource(source)) { @@ -5735,10 +5791,31 @@ function copySourcesToTexture(device, texture, sources, options = {}) { } else { const s = source; + // work around limit that you can't call copyExternalImageToTexture for 3d texture. + // sse https://github.com/gpuweb/gpuweb/issues/4697 for if we can remove this + let dstTexture = texture; + let copyOrigin = origin; + if (texture.dimension === '3d') { + tempTexture = tempTexture ?? device.createTexture({ + format: texture.format, + usage: texture.usage | GPUTextureUsage.COPY_SRC, + size: [texture.width, texture.height, 1], + }); + dstTexture = tempTexture; + copyOrigin = [0, 0, 0]; + } const { flipY, premultipliedAlpha, colorSpace } = options; - device.queue.copyExternalImageToTexture({ source: s, flipY, }, { texture, premultipliedAlpha, colorSpace, origin }, getSizeFromSource(s, options)); + device.queue.copyExternalImageToTexture({ source: s, flipY, }, { texture: dstTexture, premultipliedAlpha, colorSpace, origin: copyOrigin }, getSizeFromSource(s, options)); + if (tempTexture) { + const encoder = device.createCommandEncoder(); + encoder.copyTextureToTexture({ texture: tempTexture }, { texture, origin }, tempTexture); + device.queue.submit([encoder.finish()]); + } } }); + if (tempTexture) { + tempTexture.destroy(); + } if (texture.mipLevelCount > 1) { generateMipmap(device, texture); } @@ -6878,7 +6955,7 @@ function generateTriangleNormals(positions) { return normals; } -var primitives = /*#__PURE__*/Object.freeze({ +var primitives = { __proto__: null, TypedArrayWrapper: TypedArrayWrapper, create3DFVertices: create3DFVertices, @@ -6892,7 +6969,7 @@ var primitives = /*#__PURE__*/Object.freeze({ createXYQuadVertices: createXYQuadVertices, deindex: deindex, generateTriangleNormals: generateTriangleNormals -}); +}; -export { TypedArrayViewGenerator, copySourceToTexture, copySourcesToTexture, createBufferLayoutsFromArrays, createBuffersAndAttributesFromArrays, createTextureFromImage, createTextureFromImages, createTextureFromSource, createTextureFromSources, drawArrays, generateMipmap, getNumComponents, getSizeAndAlignmentOfUnsizedArrayElement, getSizeForMipFromTexture, getSizeFromSource, interleaveVertexData, isTypedArray, kTypes, loadImageBitmap, makeBindGroupLayoutDescriptors, makeShaderDataDefinitions, makeStructuredView, makeTypedArrayFromArrayUnion, makeTypedArrayViews, normalizeGPUExtent3D, numMipLevels, primitives, setIntrinsicsToView, setStructuredValues, setStructuredView, setTypedValues, setVertexAndIndexBuffers, subarray }; +export { TypedArrayViewGenerator, copySourceToTexture, copySourcesToTexture, createBufferLayoutsFromArrays, createBuffersAndAttributesFromArrays, createTextureFromImage, createTextureFromImages, createTextureFromSource, createTextureFromSources, drawArrays, generateMipmap, getNumComponents, getSizeAndAlignmentOfUnsizedArrayElement, getSizeForMipFromTexture, getSizeFromSource, interleaveVertexData, isTypedArray, loadImageBitmap, makeBindGroupLayoutDescriptors, makeShaderDataDefinitions, makeStructuredView, makeTypedArrayFromArrayUnion, makeTypedArrayViews, normalizeGPUExtent3D, numMipLevels, primitives, setIntrinsicsToView, setStructuredValues, setStructuredView, setTypedValues, setVertexAndIndexBuffers, subarray }; //# sourceMappingURL=webgpu-utils.module.js.map diff --git a/test/tests/data-byte-diagram-tests.js b/test/tests/data-byte-diagram-tests.js index 6713e959..ebeec497 100644 --- a/test/tests/data-byte-diagram-tests.js +++ b/test/tests/data-byte-diagram-tests.js @@ -168,8 +168,8 @@ describe('data-byte-diagram-tests', () => { @group(0) @binding(5) var texExt: texture_external; @group(1) @binding(0) var u: mat4x4f; @group(1) @binding(1) var s: mat4x4f; - @group(1) @binding(2) var sr: mat4x4f; - @group(1) @binding(3) var srw: mat4x4f; + @group(1) @binding(2) var sr: array; + @group(1) @binding(3) var srw: array; @group(3) @binding(0) var tsR: texture_storage_2d; @group(3) @binding(1) var tsW: texture_storage_2d; @group(3) @binding(2) var tsRW: texture_storage_2d; @@ -242,13 +242,16 @@ describe('data-byte-diagram-tests', () => { { binding: 0, visibility: GPUShaderStage.COMPUTE, - buffer: {}, + buffer: { + minBindingSize: 64, + }, }, { binding: 1, visibility: GPUShaderStage.COMPUTE, buffer: { type: "read-only-storage", + minBindingSize: 64, }, }, { @@ -256,6 +259,7 @@ describe('data-byte-diagram-tests', () => { visibility: GPUShaderStage.COMPUTE, buffer: { type: "read-only-storage", + minBindingSize: 160, }, }, { @@ -311,8 +315,8 @@ describe('data-byte-diagram-tests', () => { @group(0) @binding(5) var texExt: texture_external; @group(1) @binding(0) var u: mat4x4f; @group(1) @binding(1) var s: mat4x4f; - @group(1) @binding(2) var sr: mat4x4f; - @group(1) @binding(3) var srw: mat4x4f; + @group(1) @binding(2) var sr: array; + @group(1) @binding(3) var srw: array; @group(3) @binding(0) var tsR: texture_storage_2d; @group(3) @binding(1) var tsW: texture_storage_2d; @group(3) @binding(2) var tsRW: texture_storage_2d; @@ -396,7 +400,7 @@ describe('data-byte-diagram-tests', () => { buffer: { type: "uniform", hasDynamicOffset: false, - minBindingSize: 0, + minBindingSize: 64, }, }, { @@ -405,7 +409,7 @@ describe('data-byte-diagram-tests', () => { buffer: { type: "read-only-storage", hasDynamicOffset: false, - minBindingSize: 0, + minBindingSize: 64, }, }, { @@ -414,7 +418,7 @@ describe('data-byte-diagram-tests', () => { buffer: { type: "read-only-storage", hasDynamicOffset: false, - minBindingSize: 0, + minBindingSize: 160, }, }, {