From 58d51090746647c523ddda207a5ecd5cc3cc1d58 Mon Sep 17 00:00:00 2001 From: Greggman Date: Mon, 9 Dec 2024 16:29:31 -0800 Subject: [PATCH 1/4] Add back unicode for texture diagrams (#4074) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Example: ``` 0 1 2 3 ╔═══╤═══╦═══╤═══╗ 0 ║ a │ ║ │ ║ ╟───┼───╫───┼───╢ 1 ║ │ ║ │ ║ ╠═══╪═══╬═══╪═══╣ 2 ║ │ ║ │ ║ ╟───┼───╫───┼───╢ 3 ║ │ ║ │ b ║ ╚═══╧═══╩═══╧═══╝ ``` --- .../expression/call/builtin/texture_utils.ts | 123 +++++++++++++----- 1 file changed, 89 insertions(+), 34 deletions(-) diff --git a/src/webgpu/shader/execution/expression/call/builtin/texture_utils.ts b/src/webgpu/shader/execution/expression/call/builtin/texture_utils.ts index ba8a58ae861c..b9013281e2cc 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/texture_utils.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/texture_utils.ts @@ -3430,25 +3430,72 @@ async function identifySamplePoints( // example when blockWidth = 2, blockHeight = 2 // // 0 1 2 3 - // +===+===+===+===+ - // 0 # a | # | # - // +---+---+---+---+ - // 1 # | # | # - // +===+===+===+===+ - // 2 # | # | # - // +---+---+---+---+ - // 3 # | # | b # - // +===+===+===+===+ + // ╔═══╤═══╦═══╤═══╗ + // 0 ║ a │ ║ │ ║ + // ╟───┼───╫───┼───╢ + // 1 ║ │ ║ │ ║ + // ╠═══╪═══╬═══╪═══╣ + // 2 ║ │ ║ │ ║ + // ╟───┼───╫───┼───╢ + // 3 ║ │ ║ │ b ║ + // ╚═══╧═══╩═══╧═══╝ + + /* prettier-ignore */ + const blockParts = { + top: { left: '╔', fill: '═══', right: '╗', block: '╦', texel: '╤' }, + mid: { left: '╠', fill: '═══', right: '╣', block: '╬', texel: '╪' }, + bot: { left: '╚', fill: '═══', right: '╝', block: '╩', texel: '╧' }, + texelMid: { left: '╟', fill: '───', right: '╢', block: '╫', texel: '┼' }, + value: { left: '║', fill: ' ', right: '║', block: '║', texel: '│' }, + } as const; + /* prettier-ignore */ + const nonBlockParts = { + top: { left: '┌', fill: '───', right: '┐', block: '┬', texel: '┬' }, + mid: { left: '├', fill: '───', right: '┤', block: '┼', texel: '┼' }, + bot: { left: '└', fill: '───', right: '┘', block: '┴', texel: '┴' }, + texelMid: { left: '├', fill: '───', right: '┤', block: '┼', texel: '┼' }, + value: { left: '│', fill: ' ', right: '│', block: '│', texel: '│' }, + } as const; const lines: string[] = []; const letter = (idx: number) => String.fromCodePoint(idx < 30 ? 97 + idx : idx + 9600 - 30); // 97: 'a' let idCount = 0; const { blockWidth, blockHeight } = kTextureFormatInfo[texture.descriptor.format]; - const [blockHChar, blockVChar] = Math.max(blockWidth, blockHeight) > 1 ? ['=', '#'] : ['-', '|']; - const blockHCell = '+'.padStart(4, blockHChar); // generates ---+ or ===+ // range + concatenate results. const rangeCat = (num: number, fn: (i: number) => T) => range(num, fn).join(''); + const joinFn = (arr: string[], fn: (i: number) => string) => { + const joins = range(arr.length - 1, fn); + return arr.map((s, i) => `${s}${joins[i] ?? ''}`).join(''); + }; + const parts = Math.max(blockWidth, blockHeight) > 1 ? blockParts : nonBlockParts; + /** + * Makes a row that's [left, fill, texel, fill, block, fill, texel, fill, right] + * except if `contents` is supplied then it would be + * [left, contents[0], texel, contents[1], block, contents[2], texel, contents[3], right] + */ + const makeRow = ( + blockPaddedWidth: number, + width: number, + { + left, + fill, + right, + block, + texel, + }: { + left: string; + fill: string; + right: string; + block: string; + texel: string; + }, + contents?: string[] + ) => { + return `${left}${joinFn(contents ?? range(blockPaddedWidth, x => fill), x => { + return (x + 1) % blockWidth === 0 ? block : texel; + })}${right}`; + }; for (let mipLevel = 0; mipLevel < mipLevelCount; ++mipLevel) { const level = levels[mipLevel]; @@ -3478,31 +3525,39 @@ async function identifySamplePoints( continue; } + const blockPaddedHeight = align(height, blockHeight); + const blockPaddedWidth = align(width, blockWidth); lines.push(` ${rangeCat(width, x => ` ${x.toString().padEnd(2)}`)}`); - lines.push(` +${rangeCat(width, () => blockHCell)}`); - for (let y = 0; y < height; y++) { - { - let line = `${y.toString().padStart(2)} ${blockVChar}`; - for (let x = 0; x < width; x++) { - const colChar = (x + 1) % blockWidth === 0 ? blockVChar : '|'; - const texelIdx = x + y * texelsPerRow; - const weight = layerEntries.get(texelIdx); - if (weight !== undefined) { - line += ` ${letter(idCount + orderedTexelIndices.length)} ${colChar}`; - orderedTexelIndices.push(texelIdx); - } else { - line += ` ${colChar}`; - } - } - lines.push(line); - } - if (y < height - 1) { - lines.push( - ` +${rangeCat(width, () => ((y + 1) % blockHeight === 0 ? blockHCell : '---+'))}` - ); - } + lines.push(` ${makeRow(blockPaddedWidth, width, parts.top)}`); + for (let y = 0; y < blockPaddedHeight; y++) { + lines.push( + `${y.toString().padStart(2)} ${makeRow( + blockPaddedWidth, + width, + parts.value, + range(blockPaddedWidth, x => { + const texelIdx = x + y * texelsPerRow; + const weight = layerEntries.get(texelIdx); + const outside = y >= height || x >= width; + if (outside || weight === undefined) { + return outside ? '░░░' : ' '; + } else { + const id = letter(idCount + orderedTexelIndices.length); + orderedTexelIndices.push(texelIdx); + return ` ${id} `; + } + }) + )}` + ); + // It's either a block row, a texel row, or the last row. + const end = y < blockPaddedHeight - 1; + const lineParts = end + ? (y + 1) % blockHeight === 0 + ? parts.mid + : parts.texelMid + : parts.bot; + lines.push(` ${makeRow(blockPaddedWidth, width, lineParts)}`); } - lines.push(` +${range(width, () => blockHCell).join('')}`); const pad2 = (n: number) => n.toString().padStart(2); const pad3 = (n: number) => n.toString().padStart(3); From a7274c9035492494fe2db16131463fb6dd2cf0a6 Mon Sep 17 00:00:00 2001 From: Greggman Date: Tue, 10 Dec 2024 18:13:13 -0800 Subject: [PATCH 2/4] Move MaxLimitsTest to MaxLimitTestMixin (#4085) I wanted to use MaxLimitsTest with resource_compatibility.spec.ts but that file's tests are based on `CreateRenderPipelineValidationTest` which itself is based on `ValidationTest` and so would force making `ValidationTest` be based on `MaxLimitsTest`. I actually think thats a good idea. The default should be max limits. But, it breaks too many things. So, MaxLimitsTest a mixin. --- .../api/operation/rendering/draw.spec.ts | 4 +-- .../sampling/sampler_texture.spec.ts | 4 +-- .../resource_compatibility.spec.ts | 2 +- src/webgpu/gpu_test.ts | 29 +++++++++++++------ 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/webgpu/api/operation/rendering/draw.spec.ts b/src/webgpu/api/operation/rendering/draw.spec.ts index 262a96aaa7a0..b5df5297795a 100644 --- a/src/webgpu/api/operation/rendering/draw.spec.ts +++ b/src/webgpu/api/operation/rendering/draw.spec.ts @@ -11,10 +11,10 @@ import { TypedArrayBufferView, TypedArrayBufferViewConstructor, } from '../../../../common/util/util.js'; -import { MaxLimitsTest, TextureTestMixin } from '../../../gpu_test.js'; +import { GPUTest, MaxLimitsTestMixin, TextureTestMixin } from '../../../gpu_test.js'; import { PerPixelComparison } from '../../../util/texture/texture_ok.js'; -class DrawTest extends TextureTestMixin(MaxLimitsTest) { +class DrawTest extends TextureTestMixin(MaxLimitsTestMixin(GPUTest)) { checkTriangleDraw(opts: { firstIndex: number | undefined; count: number; diff --git a/src/webgpu/api/operation/sampling/sampler_texture.spec.ts b/src/webgpu/api/operation/sampling/sampler_texture.spec.ts index 1304d2d81608..4a5f2dbd3b36 100644 --- a/src/webgpu/api/operation/sampling/sampler_texture.spec.ts +++ b/src/webgpu/api/operation/sampling/sampler_texture.spec.ts @@ -7,10 +7,10 @@ Tests samplers with textures. import { makeTestGroup } from '../../../../common/framework/test_group.js'; import { assert, range } from '../../../../common/util/util.js'; -import { MaxLimitsTest, TextureTestMixin } from '../../../gpu_test.js'; +import { GPUTest, MaxLimitsTestMixin, TextureTestMixin } from '../../../gpu_test.js'; import { TexelView } from '../../../util/texture/texel_view.js'; -export const g = makeTestGroup(TextureTestMixin(MaxLimitsTest)); +export const g = makeTestGroup(TextureTestMixin(MaxLimitsTestMixin(GPUTest))); g.test('sample_texture_combos') .desc( diff --git a/src/webgpu/api/validation/render_pipeline/resource_compatibility.spec.ts b/src/webgpu/api/validation/render_pipeline/resource_compatibility.spec.ts index 8c516ebb3b85..f14211482164 100644 --- a/src/webgpu/api/validation/render_pipeline/resource_compatibility.spec.ts +++ b/src/webgpu/api/validation/render_pipeline/resource_compatibility.spec.ts @@ -1,5 +1,5 @@ export const description = ` -Tests for resource compatibilty between pipeline layout and shader modules +Tests for resource compatibility between pipeline layout and shader modules `; import { makeTestGroup } from '../../../../common/framework/test_group.js'; diff --git a/src/webgpu/gpu_test.ts b/src/webgpu/gpu_test.ts index d6d6626109d4..ffc20f5f2c22 100644 --- a/src/webgpu/gpu_test.ts +++ b/src/webgpu/gpu_test.ts @@ -1333,16 +1333,27 @@ export class MaxLimitsGPUTestSubcaseBatchState extends GPUTestSubcaseBatchState } } -/** - * A Test that requests all the max limits from the adapter on the device. - */ -export class MaxLimitsTest extends GPUTest { - public static override MakeSharedState( - recorder: TestCaseRecorder, - params: TestParams - ): GPUTestSubcaseBatchState { - return new MaxLimitsGPUTestSubcaseBatchState(recorder, params); +export type MaxLimitsTestMixinType = { + // placeholder. Change to an interface if we need MaxLimits specific methods. +}; + +export function MaxLimitsTestMixin>( + Base: F +): FixtureClassWithMixin { + class MaxLimitsImpl + extends (Base as FixtureClassInterface) + implements MaxLimitsTestMixinType + { + // + public static override MakeSharedState( + recorder: TestCaseRecorder, + params: TestParams + ): GPUTestSubcaseBatchState { + return new MaxLimitsGPUTestSubcaseBatchState(recorder, params); + } } + + return MaxLimitsImpl as unknown as FixtureClassWithMixin; } /** From 1afb7eba336735fac1716d8f9debb3532eefc7ce Mon Sep 17 00:00:00 2001 From: petermcneeleychromium <96925679+petermcneeleychromium@users.noreply.github.com> Date: Tue, 10 Dec 2024 23:11:15 -0500 Subject: [PATCH 3/4] Expand valid input for smoothstep to include low > high (#4084) Co-authored-by: Peter McNeeley --- .../shader/execution/expression/call/builtin/smoothstep.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts index f65bb951bf25..dbdc43d9f937 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts @@ -28,7 +28,7 @@ export const g = makeTestGroup(GPUTest); function validForConst(c: Case): boolean { const low = (c.input as Value[])[0] as ScalarValue; const high = (c.input as Value[])[1] as ScalarValue; - return low.value < high.value; + return low.value !== high.value; } g.test('abstract_float') From 1e545f5fc54cbb7f347effee407d76e64666bbc1 Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Wed, 11 Dec 2024 00:53:58 -0500 Subject: [PATCH 4/4] Remove many test cases that never run because maxColorAttachments=8 (#4087) * part 1: use indices instead of full data for subcase params * part 2: remove cases that are always no-ops at maxColorAttachments>8 --- .../createRenderBundleEncoder.spec.ts | 12 +++- .../attachment_compatibility.spec.ts | 70 ++++++++++--------- .../render_pass_descriptor.spec.ts | 9 ++- .../render_pipeline/fragment_state.spec.ts | 10 ++- 4 files changed, 62 insertions(+), 39 deletions(-) diff --git a/src/webgpu/api/validation/encoding/createRenderBundleEncoder.spec.ts b/src/webgpu/api/validation/encoding/createRenderBundleEncoder.spec.ts index b4beeb8fbe5d..ade00417411b 100644 --- a/src/webgpu/api/validation/encoding/createRenderBundleEncoder.spec.ts +++ b/src/webgpu/api/validation/encoding/createRenderBundleEncoder.spec.ts @@ -1,10 +1,12 @@ export const description = ` createRenderBundleEncoder validation tests. + +TODO(#3363): Make this into a MaxLimitTest and increase kMaxColorAttachments. `; import { makeTestGroup } from '../../../../common/framework/test_group.js'; import { range } from '../../../../common/util/util.js'; -import { kMaxColorAttachmentsToTest } from '../../../capability_info.js'; +import { getDefaultLimits } from '../../../capability_info.js'; import { computeBytesPerSampleFromFormats, kAllTextureFormats, @@ -14,6 +16,10 @@ import { } from '../../../format_info.js'; import { ValidationTest } from '../validation_test.js'; +// MAINTENANCE_TODO: This should be changed to kMaxColorAttachmentsToTest +// when this is made a MaxLimitTest (see above). +const kMaxColorAttachments = getDefaultLimits('core').maxColorAttachments.default; + export const g = makeTestGroup(ValidationTest); g.test('attachment_state,limits,maxColorAttachments') @@ -21,7 +27,7 @@ g.test('attachment_state,limits,maxColorAttachments') .params(u => u.beginSubcases().combine( 'colorFormatCount', - range(kMaxColorAttachmentsToTest, i => i + 1) + range(kMaxColorAttachments, i => i + 1) ) ) .fn(t => { @@ -52,7 +58,7 @@ g.test('attachment_state,limits,maxColorAttachmentBytesPerSample,aligned') .beginSubcases() .combine( 'colorFormatCount', - range(kMaxColorAttachmentsToTest, i => i + 1) + range(kMaxColorAttachments, i => i + 1) ) ) .beforeAllSubcases(t => { diff --git a/src/webgpu/api/validation/render_pass/attachment_compatibility.spec.ts b/src/webgpu/api/validation/render_pass/attachment_compatibility.spec.ts index b0a4bf226dba..8f5982f8af18 100644 --- a/src/webgpu/api/validation/render_pass/attachment_compatibility.spec.ts +++ b/src/webgpu/api/validation/render_pass/attachment_compatibility.spec.ts @@ -1,10 +1,12 @@ export const description = ` Validation for attachment compatibility between render passes, bundles, and pipelines + +TODO(#3363): Make this into a MaxLimitTest and increase kMaxColorAttachments. `; import { makeTestGroup } from '../../../../common/framework/test_group.js'; import { range } from '../../../../common/util/util.js'; -import { kMaxColorAttachmentsToTest, kTextureSampleCounts } from '../../../capability_info.js'; +import { getDefaultLimits, kTextureSampleCounts } from '../../../capability_info.js'; import { kRegularTextureFormats, kSizedDepthStencilFormats, @@ -15,7 +17,11 @@ import { } from '../../../format_info.js'; import { ValidationTest } from '../validation_test.js'; -const kColorAttachmentCounts = range(kMaxColorAttachmentsToTest, i => i + 1); +// MAINTENANCE_TODO: This should be changed to kMaxColorAttachmentsToTest +// when this is made a MaxLimitTest (see above). +const kMaxColorAttachments = getDefaultLimits('core').maxColorAttachments.default; + +const kColorAttachmentCounts = range(kMaxColorAttachments, i => i + 1); const kColorAttachments = kColorAttachmentCounts .map(count => { // generate cases with 0..1 null attachments at different location @@ -240,26 +246,25 @@ g.test('render_pass_and_bundle,color_sparse') // introduce attachmentCount to make it easier to split the test .combine('attachmentCount', kColorAttachmentCounts) .beginSubcases() - .combine('passAttachments', kColorAttachments) - .combine('bundleAttachments', kColorAttachments) - .filter( - p => - p.attachmentCount === p.passAttachments.length && - p.attachmentCount === p.bundleAttachments.length + // Indices into kColorAttachments + .expand('iPass', p => + range(kColorAttachments.length, i => i).filter( + i => kColorAttachments[i].length === p.attachmentCount + ) + ) + .expand('iBundle', p => + range(kColorAttachments.length, i => i).filter( + i => kColorAttachments[i].length === p.attachmentCount + ) ) ) .fn(t => { - const { passAttachments, bundleAttachments } = t.params; + const passAttachments = kColorAttachments[t.params.iPass]; + const bundleAttachments = kColorAttachments[t.params.iBundle]; const { maxColorAttachments } = t.device.limits; - t.skipIf( - passAttachments.length > maxColorAttachments, - `num passAttachments: ${passAttachments.length} > maxColorAttachments for device: ${maxColorAttachments}` - ); - t.skipIf( - bundleAttachments.length > maxColorAttachments, - `num bundleAttachments: ${bundleAttachments.length} > maxColorAttachments for device: ${maxColorAttachments}` - ); + t.skipIf(passAttachments.length > maxColorAttachments); + t.skipIf(bundleAttachments.length > maxColorAttachments); const colorFormats = bundleAttachments.map(i => (i ? 'rgba8uint' : null)); const bundleEncoder = t.device.createRenderBundleEncoder({ @@ -445,25 +450,26 @@ Test that each of color attachments in render passes or bundles match that of th // introduce attachmentCount to make it easier to split the test .combine('attachmentCount', kColorAttachmentCounts) .beginSubcases() - .combine('encoderAttachments', kColorAttachments) - .combine('pipelineAttachments', kColorAttachments) - .filter( - p => - p.attachmentCount === p.encoderAttachments.length && - p.attachmentCount === p.pipelineAttachments.length + // Indices into kColorAttachments + .expand('iEncoder', p => + range(kColorAttachments.length, i => i).filter( + i => kColorAttachments[i].length === p.attachmentCount + ) + ) + .expand('iPipeline', p => + range(kColorAttachments.length, i => i).filter( + i => kColorAttachments[i].length === p.attachmentCount + ) ) ) .fn(t => { - const { encoderType, encoderAttachments, pipelineAttachments } = t.params; + const { encoderType } = t.params; + const encoderAttachments = kColorAttachments[t.params.iEncoder]; + const pipelineAttachments = kColorAttachments[t.params.iPipeline]; + const { maxColorAttachments } = t.device.limits; - t.skipIf( - encoderAttachments.length > maxColorAttachments, - `num encoderAttachments: ${encoderAttachments.length} > maxColorAttachments for device: ${maxColorAttachments}` - ); - t.skipIf( - pipelineAttachments.length > maxColorAttachments, - `num pipelineAttachments: ${pipelineAttachments.length} > maxColorAttachments for device: ${maxColorAttachments}` - ); + t.skipIf(encoderAttachments.length > maxColorAttachments); + t.skipIf(pipelineAttachments.length > maxColorAttachments); const colorTargets = pipelineAttachments.map(i => i ? ({ format: 'rgba8uint', writeMask: 0 } as GPUColorTargetState) : null diff --git a/src/webgpu/api/validation/render_pass/render_pass_descriptor.spec.ts b/src/webgpu/api/validation/render_pass/render_pass_descriptor.spec.ts index 76fca77a90bf..3ded92c5c022 100644 --- a/src/webgpu/api/validation/render_pass/render_pass_descriptor.spec.ts +++ b/src/webgpu/api/validation/render_pass/render_pass_descriptor.spec.ts @@ -1,12 +1,13 @@ export const description = ` render pass descriptor validation tests. +TODO(#3363): Make this into a MaxLimitTest and increase kMaxColorAttachments. TODO: review for completeness `; import { makeTestGroup } from '../../../../common/framework/test_group.js'; import { range } from '../../../../common/util/util.js'; -import { kMaxColorAttachmentsToTest, kQueryTypes } from '../../../capability_info.js'; +import { getDefaultLimits, kQueryTypes } from '../../../capability_info.js'; import { GPUConst } from '../../../constants.js'; import { computeBytesPerSampleFromFormats, @@ -16,6 +17,10 @@ import { } from '../../../format_info.js'; import { ValidationTest } from '../validation_test.js'; +// MAINTENANCE_TODO: This should be changed to kMaxColorAttachmentsToTest +// when this is made a MaxLimitTest (see above). +const kMaxColorAttachments = getDefaultLimits('core').maxColorAttachments.default; + class F extends ValidationTest { createTestTexture( options: { @@ -201,7 +206,7 @@ g.test('color_attachments,limits,maxColorAttachmentBytesPerSample,aligned') .beginSubcases() .combine( 'attachmentCount', - range(kMaxColorAttachmentsToTest, i => i + 1) + range(kMaxColorAttachments, i => i + 1) ) ) .beforeAllSubcases(t => { diff --git a/src/webgpu/api/validation/render_pipeline/fragment_state.spec.ts b/src/webgpu/api/validation/render_pipeline/fragment_state.spec.ts index 51a5d5f79763..efbe8b0b5b94 100644 --- a/src/webgpu/api/validation/render_pipeline/fragment_state.spec.ts +++ b/src/webgpu/api/validation/render_pipeline/fragment_state.spec.ts @@ -1,14 +1,16 @@ export const description = ` This test dedicatedly tests validation of GPUFragmentState of createRenderPipeline. + +TODO(#3363): Make this into a MaxLimitTest and increase kMaxColorAttachments. `; import { makeTestGroup } from '../../../../common/framework/test_group.js'; import { assert, range } from '../../../../common/util/util.js'; import { + getDefaultLimits, IsDualSourceBlendingFactor, kBlendFactors, kBlendOperations, - kMaxColorAttachmentsToTest, } from '../../../capability_info.js'; import { GPUConst } from '../../../constants.js'; import { @@ -28,6 +30,10 @@ import { kTexelRepresentationInfo } from '../../../util/texture/texel_data.js'; import { ColorTargetState, CreateRenderPipelineValidationTest } from './common.js'; +// MAINTENANCE_TODO: This should be changed to kMaxColorAttachmentsToTest +// when this is made a MaxLimitTest (see above). +const kMaxColorAttachments = getDefaultLimits('core').maxColorAttachments.default; + export const g = makeTestGroup(CreateRenderPipelineValidationTest); const values = [0, 1, 0, 1]; @@ -169,7 +175,7 @@ g.test('limits,maxColorAttachmentBytesPerSample,aligned') .beginSubcases() .combine( 'attachmentCount', - range(kMaxColorAttachmentsToTest, i => i + 1) + range(kMaxColorAttachments, i => i + 1) ) .combine('isAsync', [false, true]) )