From 58d51090746647c523ddda207a5ecd5cc3cc1d58 Mon Sep 17 00:00:00 2001 From: Greggman Date: Mon, 9 Dec 2024 16:29:31 -0800 Subject: [PATCH 1/2] 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/2] 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; } /**