diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index f51eab5f4838..a2bc8ac6adf3 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -1449,10 +1449,10 @@ "webgpu:shader,execution,expression,call,builtin,tanh:abstract_float:*": { "subcaseMS": 18.406 }, "webgpu:shader,execution,expression,call,builtin,tanh:f16:*": { "subcaseMS": 75.982 }, "webgpu:shader,execution,expression,call,builtin,tanh:f32:*": { "subcaseMS": 32.719 }, - "webgpu:shader,execution,expression,call,builtin,textureDimension:depth:*": { "subcaseMS": 20.801 }, - "webgpu:shader,execution,expression,call,builtin,textureDimension:external:*": { "subcaseMS": 1.700 }, - "webgpu:shader,execution,expression,call,builtin,textureDimension:sampled:*": { "subcaseMS": 16.506 }, - "webgpu:shader,execution,expression,call,builtin,textureDimension:storage:*": { "subcaseMS": 25.907 }, + "webgpu:shader,execution,expression,call,builtin,textureDimensions:depth:*": { "subcaseMS": 20.801 }, + "webgpu:shader,execution,expression,call,builtin,textureDimensions:external:*": { "subcaseMS": 1.700 }, + "webgpu:shader,execution,expression,call,builtin,textureDimensions:sampled_and_multisampled:*": { "subcaseMS": 16.506 }, + "webgpu:shader,execution,expression,call,builtin,textureDimensions:storage:*": { "subcaseMS": 25.907 }, "webgpu:shader,execution,expression,call,builtin,textureGather:depth_2d_coords:*": { "subcaseMS": 11.601 }, "webgpu:shader,execution,expression,call,builtin,textureGather:depth_3d_coords:*": { "subcaseMS": 2.200 }, "webgpu:shader,execution,expression,call,builtin,textureGather:depth_array_2d_coords:*": { "subcaseMS": 23.801 }, diff --git a/src/webgpu/shader/execution/expression/call/builtin/textureDimension.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/textureDimension.spec.ts deleted file mode 100644 index 0ecb9964cf8e..000000000000 --- a/src/webgpu/shader/execution/expression/call/builtin/textureDimension.spec.ts +++ /dev/null @@ -1,160 +0,0 @@ -export const description = ` -Execution tests for the 'textureDimension' builtin function - -The dimensions of the texture in texels. -For textures based on cubes, the results are the dimensions of each face of the cube. -Cube faces are square, so the x and y components of the result are equal. -If level is outside the range [0, textureNumLevels(t)) then any valid value for the return type may be returned. -`; - -import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; -import { GPUTest } from '../../../../../gpu_test.js'; - -export const g = makeTestGroup(GPUTest); - -g.test('sampled') - .specURL('https://www.w3.org/TR/WGSL/#texturedimensions') - .desc( - ` -T: f32, i32, u32 - -fn textureDimensions(t: texture_1d) -> u32 -fn textureDimensions(t: texture_1d, level: u32) -> u32 -fn textureDimensions(t: texture_2d) -> vec2 -fn textureDimensions(t: texture_2d, level: u32) -> vec2 -fn textureDimensions(t: texture_2d_array) -> vec2 -fn textureDimensions(t: texture_2d_array, level: u32) -> vec2 -fn textureDimensions(t: texture_3d) -> vec3 -fn textureDimensions(t: texture_3d, level: u32) -> vec3 -fn textureDimensions(t: texture_cube) -> vec2 -fn textureDimensions(t: texture_cube, level: u32) -> vec2 -fn textureDimensions(t: texture_cube_array) -> vec2 -fn textureDimensions(t: texture_cube_array, level: u32) -> vec2 -fn textureDimensions(t: texture_multisampled_2d)-> vec2 - -Parameters: - * t: the sampled texture - * level: - - The mip level, with level 0 containing a full size version of the texture. - - If omitted, the dimensions of level 0 are returned. -` - ) - .params(u => - u - .combine('texture_type', [ - 'texture_1d', - 'texture_2d', - 'texture_2d_array', - 'texture_3d', - 'texture_cube', - 'texture_cube_array', - 'texture_multisampled_2d', - ] as const) - .beginSubcases() - .combine('sampled_type', ['f32-only', 'i32', 'u32'] as const) - .combine('level', [undefined, 0, 1, 'textureNumLevels', 'textureNumLevels+1'] as const) - ) - .unimplemented(); - -g.test('depth') - .specURL('https://www.w3.org/TR/WGSL/#texturedimensions') - .desc( - ` -fn textureDimensions(t: texture_depth_2d) -> vec2 -fn textureDimensions(t: texture_depth_2d, level: u32) -> vec2 -fn textureDimensions(t: texture_depth_2d_array) -> vec2 -fn textureDimensions(t: texture_depth_2d_array, level: u32) -> vec2 -fn textureDimensions(t: texture_depth_cube) -> vec2 -fn textureDimensions(t: texture_depth_cube, level: u32) -> vec2 -fn textureDimensions(t: texture_depth_cube_array) -> vec2 -fn textureDimensions(t: texture_depth_cube_array, level: u32) -> vec2 -fn textureDimensions(t: texture_depth_multisampled_2d)-> vec2 - -Parameters: - * t: the depth or multisampled texture - * level: - - The mip level, with level 0 containing a full size version of the texture. - - If omitted, the dimensions of level 0 are returned. -` - ) - .params(u => - u - .combine('texture_type', [ - 'texture_depth_2d', - 'texture_depth_2d_array', - 'texture_depth_cube', - 'texture_depth_cube_array', - 'texture_depth_multisampled_2d', - ]) - .beginSubcases() - .combine('level', [undefined, 0, 1, 'textureNumLevels', 'textureNumLevels+1'] as const) - ) - .unimplemented(); - -g.test('storage') - .specURL('https://www.w3.org/TR/WGSL/#texturedimensions') - .desc( - ` -F: rgba8unorm - rgba8snorm - rgba8uint - rgba8sint - rgba16uint - rgba16sint - rgba16float - r32uint - r32sint - r32float - rg32uint - rg32sint - rg32float - rgba32uint - rgba32sint - rgba32float -A: read, write, read_write - -fn textureDimensions(t: texture_storage_1d) -> u32 -fn textureDimensions(t: texture_storage_2d) -> vec2 -fn textureDimensions(t: texture_storage_2d_array) -> vec2 -fn textureDimensions(t: texture_storage_3d) -> vec3 - -Parameters: - * t: the storage texture -` - ) - .params(u => - u - .combine('texel_format', [ - 'rgba8unorm', - 'rgba8snorm', - 'rgba8uint', - 'rgba8sint', - 'rgba16uint', - 'rgba16sint', - 'rgba16float', - 'r32uint', - 'r32sint', - 'r32float', - 'rg32uint', - 'rg32sint', - 'rg32float', - 'rgba32uint', - 'rgba32sint', - 'rgba32float', - ] as const) - .beginSubcases() - .combine('access_mode', ['read', 'write', 'read_write'] as const) - ) - .unimplemented(); - -g.test('external') - .specURL('https://www.w3.org/TR/WGSL/#texturedimensions') - .desc( - ` -fn textureDimensions(t: texture_external) -> vec2 - -Parameters: - * t: the external texture -` - ) - .unimplemented(); diff --git a/src/webgpu/shader/execution/expression/call/builtin/textureDimensions.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/textureDimensions.spec.ts new file mode 100644 index 000000000000..041d8bec4470 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/textureDimensions.spec.ts @@ -0,0 +1,514 @@ +export const description = ` +Execution tests for the 'textureDimensions' builtin function + +The dimensions of the texture in texels. +For textures based on cubes, the results are the dimensions of each face of the cube. +Cube faces are square, so the x and y components of the result are equal. +If level is outside the range [0, textureNumLevels(t)) then any valid value for the return type may be returned. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { + kAllTextureFormats, + kColorTextureFormats, + kTextureFormatInfo, + textureDimensionAndFormatCompatible, +} from '../../../../../format_info.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +/// The maximum number of texture mipmap levels to test. +/// Keep this small to reduce memory and test permutations. +const kMaxMipsForTest = 3; + +/// The maximum number of texture samples to test. +const kMaxSamplesForTest = 4; + +/// All the possible GPUTextureViewDimensions. +const kAllViewDimensions: readonly GPUTextureViewDimension[] = [ + '1d', + '2d', + '2d-array', + '3d', + 'cube', + 'cube-array', +] as const; + +/** @returns the aspects to test for the given format */ +function aspectsForFormat(format: GPUTextureFormat): readonly GPUTextureAspect[] { + const formatInfo = kTextureFormatInfo[format]; + if (formatInfo.depth !== undefined && formatInfo.stencil !== undefined) { + return ['depth-only', 'stencil-only']; + } + return ['all']; +} + +/** @returns the sample counts to test for the given format */ +function samplesForFormat(format: GPUTextureFormat): readonly number[] { + const info = kTextureFormatInfo[format]; + return info.multisample ? [1, kMaxSamplesForTest] : [1]; +} + +/** + * @returns a list of number of texture mipmap levels to test, given the format, view dimensions and + * number of samples. + */ +function textureMipCount(params: { + format: GPUTextureFormat; + dimensions: GPUTextureViewDimension; + samples?: number; +}): readonly number[] { + if (params.samples !== undefined && params.samples !== 1) { + // https://www.w3.org/TR/webgpu/#texture-creation + // If descriptor.sampleCount > 1: descriptor.mipLevelCount must be 1. + return [1]; + } + if (textureDimensionsForViewDimensions(params.dimensions) === '1d') { + // https://www.w3.org/TR/webgpu/#dom-gputexturedimension-2d + // Only "2d" textures may have mipmaps, be multisampled, use a compressed or depth/stencil + // format, and be used as a render attachment. + return [1]; + } + return [1, kMaxMipsForTest]; +} + +/** + * @returns a list of GPUTextureViewDescriptor.baseMipLevel to test, give the texture mipmap count. + */ +function baseMipLevel(params: { textureMipCount: number }): readonly number[] { + const out: number[] = []; + for (let i = 0; i < params.textureMipCount; i++) { + out.push(i); + } + return out; +} + +/** + * @returns the argument values for the textureDimensions() `level` parameter to test. + * An `undefined` represents a call to textureDimensions() without the level argument. + */ +function textureDimensionsLevel(params: { + samples?: number; + textureMipCount: number; + baseMipLevel: number; +}): readonly (number | undefined)[] { + if (params.samples !== undefined && params.samples > 1) { + return [undefined]; // textureDimensions() overload with `level` not available. + } + const out: (number | undefined)[] = [undefined]; + for (let i = 0; i < params.textureMipCount - params.baseMipLevel; i++) { + out.push(i); + } + return out; +} + +/** @returns the GPUTextureViewDimensions to test for the format and number of samples */ +function viewDimensions(params: { + format: GPUTextureFormat; + samples?: number; +}): readonly GPUTextureViewDimension[] { + if (params.samples !== undefined && params.samples > 1) { + // https://www.w3.org/TR/webgpu/#dom-gputexturedimension-2d + // Only 2d textures can be multisampled + return ['2d']; + } + + return kAllViewDimensions.filter(dim => + textureDimensionAndFormatCompatible(textureDimensionsForViewDimensions(dim), params.format) + ); +} + +/** @returns the GPUTextureDimension for the GPUTextureViewDimension */ +function textureDimensionsForViewDimensions(dim: GPUTextureViewDimension): GPUTextureDimension { + switch (dim) { + case '1d': + return '1d'; + case '2d': + case '2d-array': + case 'cube': + case 'cube-array': + return '2d'; + case '3d': + return '3d'; + } +} + +/** TestValues holds the texture size and expected return value of textureDimensions() */ +type TestValues = { + /** The value to pass to GPUTextureDescriptor.size, when creating the texture */ + size: number[]; + /** The expected result of calling textureDimensions() */ + expected: number[]; +}; + +/** @returns The TestValues to use for the given texture dimensions and format */ +function testValues(params: { + dimensions: GPUTextureViewDimension; + format: GPUTextureFormat; + baseMipLevel: number; + textureDimensionsLevel?: number; +}): TestValues { + // The minimum dimension length, given the number of mipmap levels that are being tested. + const kMinLen = 1 << kMaxMipsForTest; + const kNumCubeFaces = 6; + + const formatInfo = kTextureFormatInfo[params.format]; + const bw = formatInfo.blockWidth; + const bh = formatInfo.blockHeight; + let mip = params.baseMipLevel; + if (params.textureDimensionsLevel !== undefined) { + mip += params.textureDimensionsLevel; + } + + // Magic constants to multiply the minimum texture dimensions with, to provide + // different dimension values in the test. These could be parameterized, but + // these are currently fixed to reduce the number of test parameterizations. + const kMultipleA = 2; + const kMultipleB = 3; + const kMultipleC = 4; + + switch (params.dimensions) { + case '1d': { + const w = Math.max(bw, kMinLen) * kMultipleA; + return { size: [w], expected: [w >>> mip] }; + } + case '2d': { + const w = Math.max(bw, kMinLen) * kMultipleA; + const h = Math.max(bh, kMinLen) * kMultipleB; + return { size: [w, h], expected: [w >>> mip, h >>> mip] }; + } + case '2d-array': { + const w = Math.max(bw, kMinLen) * kMultipleC; + const h = Math.max(bh, kMinLen) * kMultipleB; + return { size: [w, h, 4], expected: [w >>> mip, h >>> mip] }; + } + case '3d': { + const w = Math.max(bw, kMinLen) * kMultipleA; + const h = Math.max(bh, kMinLen) * kMultipleB; + const d = kMinLen * kMultipleC; + return { + size: [w, h, d], + expected: [w >>> mip, h >>> mip, d >>> mip], + }; + } + case 'cube': { + const l = Math.max(bw, kMinLen) * Math.max(bh, kMinLen) * kMultipleB; + return { + size: [l, l, kNumCubeFaces], + expected: [l >>> mip, l >>> mip], + }; + } + case 'cube-array': { + const l = Math.max(bw, kMinLen) * Math.max(bh, kMinLen) * kMultipleC; + return { + size: [l, l, kNumCubeFaces * 3], + expected: [l >>> mip, l >>> mip], + }; + } + } +} + +/** + * Builds a shader module with the texture view bound to the WGSL texture with the given WGSL type, + * which calls textureDimensions(), assigning the result to an output buffer. + * This shader is executed with a compute shader, and the output buffer is compared to + * `values.expected`. + */ +function run( + t: GPUTest, + view: GPUTextureView, + textureType: string, + levelArg: number | undefined, + values: TestValues +) { + const outputType = values.expected.length > 1 ? `vec${values.expected.length}u` : 'u32'; + const wgsl = ` +@group(0) @binding(0) var texture : ${textureType}; +@group(0) @binding(1) var output : ${outputType}; + +@compute @workgroup_size(1) +fn main() { +output = ${ + levelArg !== undefined + ? `textureDimensions(texture, ${levelArg})` + : 'textureDimensions(texture)' + }; +} +`; + const module = t.device.createShaderModule({ + code: wgsl, + }); + const pipeline = t.device.createComputePipeline({ + compute: { module }, + layout: 'auto', + }); + const outputBuffer = t.device.createBuffer({ + size: 32, + usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.STORAGE, + }); + const bindgroup = t.device.createBindGroup({ + layout: pipeline.getBindGroupLayout(0), + entries: [ + { binding: 0, resource: view }, + { binding: 1, resource: { buffer: outputBuffer } }, + ], + }); + const encoder = t.device.createCommandEncoder(); + const pass = encoder.beginComputePass(); + pass.setPipeline(pipeline); + pass.setBindGroup(0, bindgroup); + pass.dispatchWorkgroups(1); + pass.end(); + t.device.queue.submit([encoder.finish()]); + + t.expectGPUBufferValuesEqual(outputBuffer, new Uint32Array(values.expected)); +} + +/** @returns true if the GPUTextureViewDimension is valid for a storage texture */ +function dimensionsValidForStorage(dimensions: GPUTextureViewDimension) { + switch (dimensions) { + case '1d': + case '2d': + case '2d-array': + case '3d': + return true; + default: + return false; + } +} + +g.test('sampled_and_multisampled') + .specURL('https://www.w3.org/TR/WGSL/#texturedimensions') + .desc( + ` +T: f32, i32, u32 + +fn textureDimensions(t: texture_1d) -> u32 +fn textureDimensions(t: texture_1d, level: u32) -> u32 +fn textureDimensions(t: texture_2d) -> vec2 +fn textureDimensions(t: texture_2d, level: u32) -> vec2 +fn textureDimensions(t: texture_2d_array) -> vec2 +fn textureDimensions(t: texture_2d_array, level: u32) -> vec2 +fn textureDimensions(t: texture_3d) -> vec3 +fn textureDimensions(t: texture_3d, level: u32) -> vec3 +fn textureDimensions(t: texture_cube) -> vec2 +fn textureDimensions(t: texture_cube, level: u32) -> vec2 +fn textureDimensions(t: texture_cube_array) -> vec2 +fn textureDimensions(t: texture_cube_array, level: u32) -> vec2 +fn textureDimensions(t: texture_multisampled_2d)-> vec2 + +Parameters: + * t: the sampled texture + * level: + - The mip level, with level 0 containing a full size version of the texture. + - If omitted, the dimensions of level 0 are returned. +` + ) + .params(u => + u + .combine('format', kAllTextureFormats) + .unless(u => kTextureFormatInfo[u.format].sampleType === 'unfilterable-float') + .expand('aspect', u => aspectsForFormat(u.format)) + .expand('samples', u => samplesForFormat(u.format)) + .beginSubcases() + .expand('dimensions', viewDimensions) + .expand('textureMipCount', textureMipCount) + .expand('baseMipLevel', baseMipLevel) + .expand('textureDimensionsLevel', textureDimensionsLevel) + ) + .beforeAllSubcases(t => { + const info = kTextureFormatInfo[t.params.format]; + t.skipIfTextureFormatNotSupported(t.params.format); + t.selectDeviceOrSkipTestCase(info.feature); + }) + .fn(t => { + const values = testValues(t.params); + const texture = t.device.createTexture({ + size: values.size, + dimension: textureDimensionsForViewDimensions(t.params.dimensions), + usage: + t.params.samples === 1 + ? GPUTextureUsage.TEXTURE_BINDING + : GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT, + format: t.params.format, + sampleCount: t.params.samples, + mipLevelCount: t.params.textureMipCount, + }); + const textureView = texture.createView({ + dimension: t.params.dimensions, + aspect: t.params.aspect, + baseMipLevel: t.params.baseMipLevel, + }); + + function wgslSampledTextureType(): string { + const base = t.params.samples !== 1 ? 'texture_multisampled' : 'texture'; + const dimensions = t.params.dimensions.replace('-', '_'); + if (t.params.aspect === 'stencil-only') { + return `${base}_${dimensions}`; + } + const formatInfo = kTextureFormatInfo[t.params.format]; + switch (formatInfo.sampleType) { + case 'depth': + case 'float': + return `${base}_${dimensions}`; + case 'uint': + return `${base}_${dimensions}`; + case 'sint': + return `${base}_${dimensions}`; + case 'unfilterable-float': + throw new Error(`'${t.params.format}' does not support sampling`); + } + } + + run(t, textureView, wgslSampledTextureType(), t.params.textureDimensionsLevel, values); + }); + +g.test('depth') + .specURL('https://www.w3.org/TR/WGSL/#texturedimensions') + .desc( + ` +fn textureDimensions(t: texture_depth_2d) -> vec2 +fn textureDimensions(t: texture_depth_2d, level: u32) -> vec2 +fn textureDimensions(t: texture_depth_2d_array) -> vec2 +fn textureDimensions(t: texture_depth_2d_array, level: u32) -> vec2 +fn textureDimensions(t: texture_depth_cube) -> vec2 +fn textureDimensions(t: texture_depth_cube, level: u32) -> vec2 +fn textureDimensions(t: texture_depth_cube_array) -> vec2 +fn textureDimensions(t: texture_depth_cube_array, level: u32) -> vec2 +fn textureDimensions(t: texture_depth_multisampled_2d)-> vec2 + +Parameters: + * t: the depth or multisampled texture + * level: + - The mip level, with level 0 containing a full size version of the texture. + - If omitted, the dimensions of level 0 are returned. +` + ) + .params(u => + u + .combine('format', kAllTextureFormats) + .filter(u => kTextureFormatInfo[u.format].sampleType === 'depth') + .expand('aspect', u => aspectsForFormat(u.format)) + .unless(u => u.aspect === 'stencil-only') + .expand('samples', u => samplesForFormat(u.format)) + .beginSubcases() + .expand('dimensions', viewDimensions) + .expand('textureMipCount', textureMipCount) + .expand('baseMipLevel', baseMipLevel) + .expand('textureDimensionsLevel', textureDimensionsLevel) + ) + .beforeAllSubcases(t => { + const info = kTextureFormatInfo[t.params.format]; + t.skipIfTextureFormatNotSupported(t.params.format); + t.selectDeviceOrSkipTestCase(info.feature); + }) + .fn(t => { + const values = testValues(t.params); + const texture = t.device.createTexture({ + size: values.size, + dimension: textureDimensionsForViewDimensions(t.params.dimensions), + usage: + t.params.samples === 1 + ? GPUTextureUsage.TEXTURE_BINDING + : GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT, + format: t.params.format, + sampleCount: t.params.samples, + mipLevelCount: t.params.textureMipCount, + }); + const textureView = texture.createView({ + dimension: t.params.dimensions, + aspect: t.params.aspect, + baseMipLevel: t.params.baseMipLevel, + }); + + function wgslDepthTextureType(): string { + const base = t.params.samples !== 1 ? 'texture_depth_multisampled' : 'texture_depth'; + const dimensions = t.params.dimensions.replace('-', '_'); + return `${base}_${dimensions}`; + } + + run(t, textureView, wgslDepthTextureType(), t.params.textureDimensionsLevel, values); + }); + +g.test('storage') + .specURL('https://www.w3.org/TR/WGSL/#texturedimensions') + .desc( + ` +F: rgba8unorm + rgba8snorm + rgba8uint + rgba8sint + rgba16uint + rgba16sint + rgba16float + r32uint + r32sint + r32float + rg32uint + rg32sint + rg32float + rgba32uint + rgba32sint + rgba32float +A: read, write, read_write + +fn textureDimensions(t: texture_storage_1d) -> u32 +fn textureDimensions(t: texture_storage_2d) -> vec2 +fn textureDimensions(t: texture_storage_2d_array) -> vec2 +fn textureDimensions(t: texture_storage_3d) -> vec3 + +Parameters: + * t: the storage texture +` + ) + .params(u => + u + .combine('format', kColorTextureFormats) + .filter(p => kTextureFormatInfo[p.format].color?.storage === true) + .expand('aspect', u => aspectsForFormat(u.format)) + .beginSubcases() + .expand('dimensions', u => viewDimensions(u).filter(dimensionsValidForStorage)) + .expand('textureMipCount', textureMipCount) + .expand('baseMipLevel', baseMipLevel) + ) + .beforeAllSubcases(t => { + const info = kTextureFormatInfo[t.params.format]; + t.skipIfTextureFormatNotSupported(t.params.format); + t.selectDeviceOrSkipTestCase(info.feature); + }) + .fn(t => { + const values = testValues(t.params); + const texture = t.device.createTexture({ + size: values.size, + dimension: textureDimensionsForViewDimensions(t.params.dimensions), + usage: GPUTextureUsage.STORAGE_BINDING, + format: t.params.format, + mipLevelCount: t.params.textureMipCount, + }); + const textureView = texture.createView({ + dimension: t.params.dimensions, + aspect: t.params.aspect, + mipLevelCount: 1, + baseMipLevel: t.params.baseMipLevel, + }); + + function wgslStorageTextureType(): string { + const dimensions = t.params.dimensions.replace('-', '_'); + return `texture_storage_${dimensions}<${t.params.format}, read>`; + } + + run(t, textureView, wgslStorageTextureType(), undefined, values); + }); + +g.test('external') + .specURL('https://www.w3.org/TR/WGSL/#texturedimensions') + .desc( + ` +fn textureDimensions(t: texture_external) -> vec2 + +Parameters: + * t: the external texture +` + ) + .unimplemented();