Refactor shader/execution/* (#4223)

Issue #4178
diff --git a/src/webgpu/shader/execution/float_parse.spec.ts b/src/webgpu/shader/execution/float_parse.spec.ts
index c532bfe..00d9250 100644
--- a/src/webgpu/shader/execution/float_parse.spec.ts
+++ b/src/webgpu/shader/execution/float_parse.spec.ts
@@ -5,9 +5,9 @@
 import { makeTestGroup } from '../../../common/framework/test_group.js';
 import { keysOf } from '../../../common/util/data_tables.js';
 import { iterRange } from '../../../common/util/util.js';
-import { GPUTest } from '../../gpu_test.js';
+import { AllFeaturesMaxLimitsGPUTest, GPUTest } from '../../gpu_test.js';
 
-export const g = makeTestGroup(GPUTest);
+export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);
 
 /**
  * Run a shader and check that the buffer output matches expectations.
diff --git a/src/webgpu/shader/execution/limits.spec.ts b/src/webgpu/shader/execution/limits.spec.ts
index 3c11569..d3262cd 100644
--- a/src/webgpu/shader/execution/limits.spec.ts
+++ b/src/webgpu/shader/execution/limits.spec.ts
@@ -3,10 +3,10 @@
 import { makeTestGroup } from '../../../common/framework/test_group.js';
 import { keysOf } from '../../../common/util/data_tables.js';
 import { iterRange } from '../../../common/util/util.js';
-import { GPUTest } from '../../gpu_test.js';
+import { AllFeaturesMaxLimitsGPUTest, GPUTest } from '../../gpu_test.js';
 import { CheckElementsGenerator, checkElementsEqualGenerated } from '../../util/check_contents.js';
 
-export const g = makeTestGroup(GPUTest);
+export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);
 
 // The limits that we test.
 const kMaxStructMembers = 1023;
diff --git a/src/webgpu/shader/execution/memory_layout.spec.ts b/src/webgpu/shader/execution/memory_layout.spec.ts
index 0f1d92f..8ffd741 100644
--- a/src/webgpu/shader/execution/memory_layout.spec.ts
+++ b/src/webgpu/shader/execution/memory_layout.spec.ts
@@ -3,9 +3,9 @@
 import { makeTestGroup } from '../../../common/framework/test_group.js';
 import { keysOf } from '../../../common/util/data_tables.js';
 import { iterRange } from '../../../common/util/util.js';
-import { GPUTest } from '../../gpu_test.js';
+import { AllFeaturesMaxLimitsGPUTest } from '../../gpu_test.js';
 
-export const g = makeTestGroup(GPUTest);
+export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);
 
 interface LayoutCase {
   type: string;
@@ -928,9 +928,6 @@
   )
   .beforeAllSubcases(t => {
     const testcase = kLayoutCases[t.params.case];
-    if (testcase.f16) {
-      t.selectDeviceOrSkipTestCase('shader-f16');
-    }
     // Don't test atomics in workgroup due to initialization boilerplate.
     t.skipIf(
       testcase.type.includes('atomic') && t.params.aspace !== 'storage',
@@ -944,6 +941,10 @@
   })
   .fn(t => {
     const testcase = kLayoutCases[t.params.case];
+    if (testcase.f16) {
+      t.skipIfDeviceDoesNotHaveFeature('shader-f16');
+    }
+
     let code = `
 ${testcase.f16 ? 'enable f16;' : ''}
 ${testcase.decl ?? ''}
@@ -1068,9 +1069,6 @@
   )
   .beforeAllSubcases(t => {
     const testcase = kLayoutCases[t.params.case];
-    if (testcase.f16) {
-      t.selectDeviceOrSkipTestCase('shader-f16');
-    }
     // Don't test atomics in workgroup due to initialization boilerplate.
     t.skipIf(
       testcase.type.includes('atomic') && t.params.aspace !== 'storage',
@@ -1079,6 +1077,10 @@
   })
   .fn(t => {
     const testcase = kLayoutCases[t.params.case];
+    if (testcase.f16) {
+      t.skipIfDeviceDoesNotHaveFeature('shader-f16');
+    }
+
     let code = `
 ${testcase.f16 ? 'enable f16;' : ''}
 ${testcase.decl ?? ''}
diff --git a/src/webgpu/shader/execution/padding.spec.ts b/src/webgpu/shader/execution/padding.spec.ts
index c9e2300..c472d7d 100644
--- a/src/webgpu/shader/execution/padding.spec.ts
+++ b/src/webgpu/shader/execution/padding.spec.ts
@@ -4,9 +4,9 @@
 
 import { makeTestGroup } from '../../../common/framework/test_group.js';
 import { iterRange } from '../../../common/util/util.js';
-import { GPUTest } from '../../gpu_test.js';
+import { AllFeaturesMaxLimitsGPUTest, GPUTest } from '../../gpu_test.js';
 
-export const g = makeTestGroup(GPUTest);
+export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);
 
 /**
  * Run a shader and check that the buffer output matches expectations.
@@ -272,10 +272,8 @@
      preserved.
     `
   )
-  .beforeAllSubcases(t => {
-    t.selectDeviceOrSkipTestCase('shader-f16');
-  })
   .fn(t => {
+    t.skipIfDeviceDoesNotHaveFeature('shader-f16');
     const wgsl = `
       enable f16;
       @group(0) @binding(0) var<storage, read_write> buffer : array<vec3<f16>, 4>;
@@ -315,10 +313,8 @@
      preserved.
     `
   )
-  .beforeAllSubcases(t => {
-    t.selectDeviceOrSkipTestCase('shader-f16');
-  })
   .fn(t => {
+    t.skipIfDeviceDoesNotHaveFeature('shader-f16');
     const wgsl = `
       enable f16;
       @group(0) @binding(0) var<storage, read_write> buffer : array<vec3<f16>>;
diff --git a/src/webgpu/shader/execution/robust_access.spec.ts b/src/webgpu/shader/execution/robust_access.spec.ts
index 66c7137..4541f9f 100644
--- a/src/webgpu/shader/execution/robust_access.spec.ts
+++ b/src/webgpu/shader/execution/robust_access.spec.ts
@@ -8,11 +8,11 @@
 import { makeTestGroup } from '../../../common/framework/test_group.js';
 import { assert } from '../../../common/util/util.js';
 import { Float16Array } from '../../../external/petamoriken/float16/float16.js';
-import { GPUTest } from '../../gpu_test.js';
+import { AllFeaturesMaxLimitsGPUTest, GPUTest } from '../../gpu_test.js';
 import { align } from '../../util/math.js';
 import { generateTypes, supportedScalarTypes, supportsAtomics } from '../types.js';
 
-export const g = makeTestGroup(GPUTest);
+export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);
 
 const kMaxU32 = 0xffff_ffff;
 const kMaxI32 = 0x7fff_ffff;
@@ -177,11 +177,6 @@
       .beginSubcases()
       .expandWithParams(generateTypes)
   )
-  .beforeAllSubcases(t => {
-    if (t.params.baseType === 'f16') {
-      t.selectDeviceOrSkipTestCase('shader-f16');
-    }
-  })
   .fn(async t => {
     const {
       addressSpace,
@@ -195,6 +190,9 @@
       shadowingMode,
       _kTypeInfo,
     } = t.params;
+    if (baseType === 'f16') {
+      t.skipIfDeviceDoesNotHaveFeature('shader-f16');
+    }
 
     assert(_kTypeInfo !== undefined, 'not an indexable type');
     assert('arrayLength' in _kTypeInfo);
diff --git a/src/webgpu/shader/execution/robust_access_vertex.spec.ts b/src/webgpu/shader/execution/robust_access_vertex.spec.ts
index d5792de..03a6f08 100644
--- a/src/webgpu/shader/execution/robust_access_vertex.spec.ts
+++ b/src/webgpu/shader/execution/robust_access_vertex.spec.ts
@@ -61,7 +61,7 @@
 
 import { makeTestGroup } from '../../../common/framework/test_group.js';
 import { assert } from '../../../common/util/util.js';
-import { GPUTest, TextureTestMixin } from '../../gpu_test.js';
+import { AllFeaturesMaxLimitsGPUTest, GPUTest, TextureTestMixin } from '../../gpu_test.js';
 
 // Encapsulates a draw call (either indexed or non-indexed)
 class DrawCall {
@@ -273,7 +273,7 @@
   },
 };
 
-class F extends TextureTestMixin(GPUTest) {
+class F extends TextureTestMixin(AllFeaturesMaxLimitsGPUTest) {
   generateBufferContents(
     numVertices: number,
     attributesPerBuffer: number,
diff --git a/src/webgpu/shader/execution/shadow.spec.ts b/src/webgpu/shader/execution/shadow.spec.ts
index 92ec6cc..905d9ff 100644
--- a/src/webgpu/shader/execution/shadow.spec.ts
+++ b/src/webgpu/shader/execution/shadow.spec.ts
@@ -4,9 +4,9 @@
 
 import { makeTestGroup } from '../../../common/framework/test_group.js';
 import { iterRange } from '../../../common/util/util.js';
-import { GPUTest } from '../../gpu_test.js';
+import { AllFeaturesMaxLimitsGPUTest, GPUTest } from '../../gpu_test.js';
 
-export const g = makeTestGroup(GPUTest);
+export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);
 
 /**
  * Run a shader and check that the buffer output matches expectations.
diff --git a/src/webgpu/shader/execution/stage.spec.ts b/src/webgpu/shader/execution/stage.spec.ts
index a3356b2..22e14cc 100644
--- a/src/webgpu/shader/execution/stage.spec.ts
+++ b/src/webgpu/shader/execution/stage.spec.ts
@@ -3,10 +3,10 @@
 // There are many many more shaders executed in other tests.
 
 import { makeTestGroup } from '../../../common/framework/test_group.js';
-import { GPUTest } from '../../gpu_test.js';
+import { AllFeaturesMaxLimitsGPUTest } from '../../gpu_test.js';
 import { checkElementsEqual } from '../../util/check_contents.js';
 
-export const g = makeTestGroup(GPUTest);
+export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);
 
 g.test('basic_compute')
   .desc(`Test a trivial compute shader`)
diff --git a/src/webgpu/shader/execution/value_init.spec.ts b/src/webgpu/shader/execution/value_init.spec.ts
index 842ae47..52a498a 100644
--- a/src/webgpu/shader/execution/value_init.spec.ts
+++ b/src/webgpu/shader/execution/value_init.spec.ts
@@ -1,10 +1,10 @@
 export const description = `Test that variables in the shader are value initialized`;
 
 import { makeTestGroup } from '../../../common/framework/test_group.js';
-import { GPUTest } from '../../gpu_test.js';
+import { AllFeaturesMaxLimitsGPUTest, GPUTest } from '../../gpu_test.js';
 import { Type } from '../../util/conversion.js';
 
-export const g = makeTestGroup(GPUTest);
+export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);
 
 function generateShader(
   isF16: boolean,
@@ -86,15 +86,14 @@
       .combine('addressSpace', ['private', 'function'] as const)
       .combine('type', ['bool', 'f32', 'f16', 'i32', 'u32'] as const)
   )
-  .beforeAllSubcases(t => {
-    if (t.params.type === 'f16') {
-      t.selectDeviceOrSkipTestCase('shader-f16');
-    }
-  })
   .fn(async t => {
     const typeDecl = t.params.type;
     const testValue = Type[typeDecl].create(5).wgsl();
 
+    if (typeDecl === 'f16') {
+      t.skipIfDeviceDoesNotHaveFeature('shader-f16');
+    }
+
     const comparison = `if (testVar != ${testValue}) {
       atomicStore(&output.failed, 1u);
     }`;
@@ -117,15 +116,14 @@
       .combine('type', ['bool', 'f32', 'f16', 'i32', 'u32'] as const)
       .combine('count', [2, 3, 4] as const)
   )
-  .beforeAllSubcases(t => {
-    if (t.params.type === 'f16') {
-      t.selectDeviceOrSkipTestCase('shader-f16');
-    }
-  })
   .fn(async t => {
     const typeDecl = `vec${t.params.count}<${t.params.type}>`;
     const testValue = `${typeDecl}(${Type[t.params.type].create(5).wgsl()})`;
 
+    if (typeDecl === 'f16') {
+      t.skipIfDeviceDoesNotHaveFeature('shader-f16');
+    }
+
     const comparison = `if (!all(testVar == ${testValue})) {
       atomicStore(&output.failed, 1u);
     }`;
@@ -152,15 +150,14 @@
       .combine('c', [2, 3, 4] as const)
       .combine('r', [2, 3, 4] as const)
   )
-  .beforeAllSubcases(t => {
-    if (t.params.type === 'f16') {
-      t.selectDeviceOrSkipTestCase('shader-f16');
-    }
-  })
   .fn(async t => {
     const typeDecl = `mat${t.params.c}x${t.params.r}<${t.params.type}>`;
     const testScalarValue = Type[t.params.type].create(5).wgsl();
 
+    if (typeDecl === 'f16') {
+      t.skipIfDeviceDoesNotHaveFeature('shader-f16');
+    }
+
     let testValue = `${typeDecl}(`;
     for (let c = 0; c < t.params.c; c++) {
       for (let r = 0; r < t.params.r; r++) {
@@ -194,16 +191,15 @@
       .combine('addressSpace', ['private', 'function'] as const)
       .combine('type', ['bool', 'i32', 'u32', 'f32', 'f16'] as const)
   )
-  .beforeAllSubcases(t => {
-    if (t.params.type === 'f16') {
-      t.selectDeviceOrSkipTestCase('shader-f16');
-    }
-  })
   .fn(async t => {
     const arraySize = 4;
     const typeDecl = `array<${t.params.type}, ${arraySize}>`;
     const testScalarValue = Type[t.params.type].create(5).wgsl();
 
+    if (typeDecl === 'f16') {
+      t.skipIfDeviceDoesNotHaveFeature('shader-f16');
+    }
+
     let testValue = `${typeDecl}(`;
     for (let i = 0; i < arraySize; i++) {
       testValue += `${testScalarValue},`;
@@ -233,11 +229,6 @@
       .combine('addressSpace', ['private', 'function'] as const)
       .combine('type', ['bool', 'i32', 'u32', 'f32', 'f16'] as const)
   )
-  .beforeAllSubcases(t => {
-    if (t.params.type === 'f16') {
-      t.selectDeviceOrSkipTestCase('shader-f16');
-    }
-  })
   .fn(async t => {
     const arraySize = 4;
 
@@ -245,6 +236,10 @@
     const typeDecl = `array<${innerDecl}, ${arraySize}>`;
     const testScalarValue = Type[t.params.type].create(5).wgsl();
 
+    if (typeDecl === 'f16') {
+      t.skipIfDeviceDoesNotHaveFeature('shader-f16');
+    }
+
     let testValue = `${typeDecl}(`;
     for (let i = 0; i < arraySize; i++) {
       testValue += `${innerDecl}(`;
diff --git a/src/webgpu/shader/execution/zero_init.spec.ts b/src/webgpu/shader/execution/zero_init.spec.ts
index efa46ea..fe6a3c2 100644
--- a/src/webgpu/shader/execution/zero_init.spec.ts
+++ b/src/webgpu/shader/execution/zero_init.spec.ts
@@ -2,7 +2,7 @@
 
 import { makeTestGroup } from '../../../common/framework/test_group.js';
 import { iterRange, unreachable } from '../../../common/util/util.js';
-import { GPUTest } from '../../gpu_test.js';
+import { AllFeaturesMaxLimitsGPUTest } from '../../gpu_test.js';
 import {
   ScalarType,
   kVectorContainerTypes,
@@ -47,7 +47,7 @@
   }
 }
 
-export const g = makeTestGroup(GPUTest);
+export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);
 g.test('compute,zero_init')
   .desc(
     `Test that uninitialized variables in workgroup, private, and function storage classes are initialized to zero.`