Skip to content

Commit 096f9fd

Browse files
authored
Improve fragment shader checks in subgroupBitwise (#4070)
* Modifies the fragment tests in subgroupBitwise to avoid any subgroup in the last row or column * this fixes failures seen on some devices performing subgroupXor when helpers are in bounds, but not on the edge of the framebuffer * Tests skip cases that could not possibly yield useful results, but checking is also terminated early if no useful results can be found * Enough framebuffer variants are swept to ensure some tests will cover useful subgroups, but the sizes are only known after a pipeline compile
1 parent ca6d65d commit 096f9fd

File tree

1 file changed

+52
-7
lines changed

1 file changed

+52
-7
lines changed

src/webgpu/shader/execution/expression/call/builtin/subgroupBitwise.spec.ts

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ fn main(
433433
/**
434434
* Checks bitwise ops results from a fragment shader.
435435
*
436-
* Avoids the last row and column to skip potential helper invocations.
436+
* Avoids subgroups in last row or column to skip potential helper invocations.
437437
* @param data Framebuffer output
438438
* * component 0 is result
439439
* * component 1 is generated subgroup id
@@ -453,26 +453,58 @@ function checkBitwiseFragment(
453453
): Error | undefined {
454454
const { uintsPerRow, uintsPerTexel } = getUintsPerFramebuffer(format, width, height);
455455

456-
// Iteration skips last row and column to avoid helper invocations because it is not
457-
// guaranteed whether or not they participate in the subgroup operation.
456+
// Determine if the subgroup should be included in the checks.
457+
const inBounds = new Map<number, boolean>();
458+
for (let row = 0; row < height; row++) {
459+
for (let col = 0; col < width; col++) {
460+
const offset = uintsPerRow * row + col * uintsPerTexel;
461+
const subgroup_id = data[offset + 1];
462+
if (subgroup_id === 0) {
463+
return new Error(`Internal error: helper invocation at (${col}, ${row})`);
464+
}
465+
466+
let ok = inBounds.get(subgroup_id) ?? true;
467+
ok = ok && row !== height - 1 && col !== width - 1;
468+
inBounds.set(subgroup_id, ok);
469+
}
470+
}
471+
472+
let anyInBounds = false;
473+
for (const [_, value] of inBounds) {
474+
const ok = Boolean(value);
475+
anyInBounds = anyInBounds || ok;
476+
}
477+
if (!anyInBounds) {
478+
// This variant would not reliably test behavior.
479+
return undefined;
480+
}
481+
482+
// Iteration skips subgroups in the last row or column to avoid helper
483+
// invocations because it is not guaranteed whether or not they participate
484+
// in the subgroup operation.
458485
const expected = new Map<number, number>();
459-
for (let row = 0; row < height - 1; row++) {
460-
for (let col = 0; col < width - 1; col++) {
486+
for (let row = 0; row < height; row++) {
487+
for (let col = 0; col < width; col++) {
461488
const offset = uintsPerRow * row + col * uintsPerTexel;
462489
const subgroup_id = data[offset + 1];
463490

464491
if (subgroup_id === 0) {
465492
return new Error(`Internal error: helper invocation at (${col}, ${row})`);
466493
}
467494

495+
const subgroupInBounds = inBounds.get(subgroup_id) ?? true;
496+
if (!subgroupInBounds) {
497+
continue;
498+
}
499+
468500
let v = expected.get(subgroup_id) ?? identity(op);
469501
v = bitwise(op, v, input[row * width + col]);
470502
expected.set(subgroup_id, v);
471503
}
472504
}
473505

474-
for (let row = 0; row < height - 1; row++) {
475-
for (let col = 0; col < width - 1; col++) {
506+
for (let row = 0; row < height; row++) {
507+
for (let col = 0; col < width; col++) {
476508
const offset = uintsPerRow * row + col * uintsPerTexel;
477509
const res = data[offset];
478510
const subgroup_id = data[offset + 1];
@@ -482,6 +514,11 @@ function checkBitwiseFragment(
482514
continue;
483515
}
484516

517+
const subgroupInBounds = inBounds.get(subgroup_id) ?? true;
518+
if (!subgroupInBounds) {
519+
continue;
520+
}
521+
485522
const expected_v = expected.get(subgroup_id) ?? 0;
486523
if (expected_v !== res) {
487524
return new Error(`Row ${row}, col ${col}: incorrect results:
@@ -509,6 +546,14 @@ g.test('fragment,all_active')
509546
})
510547
.fn(async t => {
511548
const numInputs = t.params.size[0] * t.params.size[1];
549+
550+
interface SubgroupLimits extends GPUSupportedLimits {
551+
minSubgroupSize: number;
552+
}
553+
const { minSubgroupSize } = t.device.limits as SubgroupLimits;
554+
const innerTexels = (t.params.size[0] - 1) * (t.params.size[1] - 1);
555+
t.skipIf(innerTexels < minSubgroupSize, 'Too few texels to be reliable');
556+
512557
const inputData = generateInputData(t.params.case, numInputs, identity(t.params.op));
513558

514559
const ident = identity(t.params.op) === 0 ? '0' : '0xffffffff';

0 commit comments

Comments
 (0)