From 824ea20e657b8a04a570f359507e499ed9dbf9b6 Mon Sep 17 00:00:00 2001
From: Shrek Shao <shrekshao@google.com>
Date: Tue, 14 Jan 2025 16:14:34 -0800
Subject: [PATCH] Compat: fix float16(32)-renderable tests

---
 .../api/operation/render_pass/storeOp.spec.ts |  1 +
 .../render_pipeline/overrides.spec.ts         |  6 ++++-
 .../pipeline_output_targets.spec.ts           |  1 +
 .../rendering/color_target_state.spec.ts      | 15 +++++++++--
 .../resource_init/texture_zero.spec.ts        |  6 ++++-
 .../operation/sampling/filter_mode.spec.ts    |  5 ++++
 src/webgpu/format_info.ts                     |  4 +++
 src/webgpu/gpu_test.ts                        | 26 +++++++++++++++++++
 .../web_platform/canvas/configure.spec.ts     |  6 ++---
 9 files changed, 63 insertions(+), 7 deletions(-)

diff --git a/src/webgpu/api/operation/render_pass/storeOp.spec.ts b/src/webgpu/api/operation/render_pass/storeOp.spec.ts
index 84a920555a17..d2c5c8b45c09 100644
--- a/src/webgpu/api/operation/render_pass/storeOp.spec.ts
+++ b/src/webgpu/api/operation/render_pass/storeOp.spec.ts
@@ -152,6 +152,7 @@ g.test('render_pass_store_op,color_attachment_only')
   )
   .beforeAllSubcases(t => {
     t.skipIfTextureFormatNotSupported(t.params.colorFormat);
+    t.skipIfFloatTextureFormatNotColorRenderable(t.params.colorFormat);
   })
   .fn(t => {
     const colorAttachment = t.createTextureTracked({
diff --git a/src/webgpu/api/operation/render_pipeline/overrides.spec.ts b/src/webgpu/api/operation/render_pipeline/overrides.spec.ts
index 61e2801d74e1..d42796e95b68 100644
--- a/src/webgpu/api/operation/render_pipeline/overrides.spec.ts
+++ b/src/webgpu/api/operation/render_pipeline/overrides.spec.ts
@@ -171,6 +171,7 @@ g.test('basic')
     );
   });
 
+const kPrecisionTestFormat = 'rgba32float';
 g.test('precision')
   .desc(`Test that the float number precision is preserved for constants`)
   .params(u =>
@@ -190,8 +191,11 @@ g.test('precision')
         },
       ])
   )
+  .beforeAllSubcases(t => {
+    t.skipIfFloatTextureFormatNotColorRenderable(kPrecisionTestFormat);
+  })
   .fn(async t => {
-    const format = 'rgba32float';
+    const format = kPrecisionTestFormat;
     await t.ExpectShaderOutputWithConstants(
       t.params.isAsync,
       format,
diff --git a/src/webgpu/api/operation/render_pipeline/pipeline_output_targets.spec.ts b/src/webgpu/api/operation/render_pipeline/pipeline_output_targets.spec.ts
index fda018a7312e..7e4c40ec5e23 100644
--- a/src/webgpu/api/operation/render_pipeline/pipeline_output_targets.spec.ts
+++ b/src/webgpu/api/operation/render_pipeline/pipeline_output_targets.spec.ts
@@ -58,6 +58,7 @@ g.test('color,attachments')
   .beforeAllSubcases(t => {
     const info = kTextureFormatInfo[t.params.format];
     t.skipIfTextureFormatNotSupported(t.params.format);
+    t.skipIfFloatTextureFormatNotColorRenderable(t.params.format);
     t.selectDeviceOrSkipTestCase(info.feature);
   })
   .fn(t => {
diff --git a/src/webgpu/api/operation/rendering/color_target_state.spec.ts b/src/webgpu/api/operation/rendering/color_target_state.spec.ts
index 5923cc1b063d..e95d9c7937d8 100644
--- a/src/webgpu/api/operation/rendering/color_target_state.spec.ts
+++ b/src/webgpu/api/operation/rendering/color_target_state.spec.ts
@@ -159,6 +159,7 @@ function computeBlendOperation(
   }
 }
 
+const kBlendingGPUBlendComponentFormat = 'rgba16float';
 g.test('blending,GPUBlendComponent')
   .desc(
     `Test all combinations of parameters for GPUBlendComponent.
@@ -202,15 +203,21 @@ g.test('blending,GPUBlendComponent')
       })
   )
   .beforeAllSubcases(t => {
+    const requiredFeatures = t.getFloatTextureFormatColorRenderableFeatures(
+      kBlendingGPUBlendComponentFormat
+    );
     if (
       IsDualSourceBlendingFactor(t.params.srcFactor) ||
       IsDualSourceBlendingFactor(t.params.dstFactor)
     ) {
-      t.selectDeviceOrSkipTestCase('dual-source-blending');
+      requiredFeatures.push('dual-source-blending');
+    }
+    if (requiredFeatures.length > 0) {
+      t.selectDeviceOrSkipTestCase(requiredFeatures);
     }
   })
   .fn(t => {
-    const textureFormat: GPUTextureFormat = 'rgba16float';
+    const textureFormat: GPUTextureFormat = kBlendingGPUBlendComponentFormat;
     const srcColor = t.params.srcColor;
     const srcColor1 = t.params.srcColor1;
     const dstColor = t.params.dstColor;
@@ -393,6 +400,7 @@ g.test('blending,formats')
   )
   .beforeAllSubcases(t => {
     t.skipIfTextureFormatNotSupported(t.params.format);
+    t.skipIfFloatTextureFormatNotColorRenderable(t.params.format);
   })
   .fn(t => {
     const { format } = t.params;
@@ -797,6 +805,9 @@ g.test('blending,clamping')
       .combine('srcValue', [0.4, 0.6, 0.8, 1.0])
       .combine('dstValue', [0.2, 0.4])
   )
+  .beforeAllSubcases(t => {
+    t.skipIfFloatTextureFormatNotColorRenderable(t.params.format);
+  })
   .fn(t => {
     const { format, srcValue, dstValue } = t.params;
 
diff --git a/src/webgpu/api/operation/resource_init/texture_zero.spec.ts b/src/webgpu/api/operation/resource_init/texture_zero.spec.ts
index 95faa5c1837d..6cb3bbfdb1d8 100644
--- a/src/webgpu/api/operation/resource_init/texture_zero.spec.ts
+++ b/src/webgpu/api/operation/resource_init/texture_zero.spec.ts
@@ -43,7 +43,11 @@ g.test('uninitialized_texture_is_zero')
   .params(kTestParams)
   .beforeAllSubcases(t => {
     t.skipIfTextureFormatNotSupported(t.params.format);
-    t.selectDeviceOrSkipTestCase(kTextureFormatInfo[t.params.format].feature);
+    const requiredFeatures = [
+      ...t.getFloatTextureFormatColorRenderableFeatures(t.params.format),
+      ...(kTextureFormatInfo[t.params.format].feature ?? []),
+    ] as GPUFeatureName[];
+    t.selectDeviceOrSkipTestCase(requiredFeatures);
   })
   .fn(t => {
     const usage = getRequiredTextureUsage(
diff --git a/src/webgpu/api/operation/sampling/filter_mode.spec.ts b/src/webgpu/api/operation/sampling/filter_mode.spec.ts
index 8d32ae5e3289..1efc2c4a3782 100644
--- a/src/webgpu/api/operation/sampling/filter_mode.spec.ts
+++ b/src/webgpu/api/operation/sampling/filter_mode.spec.ts
@@ -481,6 +481,7 @@ g.test('magFilter,nearest')
   )
   .beforeAllSubcases(t => {
     t.skipIfTextureFormatNotSupported(t.params.format);
+    t.skipIfFloatTextureFormatNotColorRenderable(t.params.format);
     if (kTextureFormatInfo[t.params.format].color.type === 'unfilterable-float') {
       t.selectDeviceOrSkipTestCase('float32-filterable');
     }
@@ -604,6 +605,7 @@ g.test('magFilter,linear')
   )
   .beforeAllSubcases(t => {
     t.skipIfTextureFormatNotSupported(t.params.format);
+    t.skipIfFloatTextureFormatNotColorRenderable(t.params.format);
     if (kTextureFormatInfo[t.params.format].color.type === 'unfilterable-float') {
       t.selectDeviceOrSkipTestCase('float32-filterable');
     }
@@ -739,6 +741,7 @@ g.test('minFilter,nearest')
   )
   .beforeAllSubcases(t => {
     t.skipIfTextureFormatNotSupported(t.params.format);
+    t.skipIfFloatTextureFormatNotColorRenderable(t.params.format);
     if (kTextureFormatInfo[t.params.format].color.type === 'unfilterable-float') {
       t.selectDeviceOrSkipTestCase('float32-filterable');
     }
@@ -872,6 +875,7 @@ g.test('minFilter,linear')
   )
   .beforeAllSubcases(t => {
     t.skipIfTextureFormatNotSupported(t.params.format);
+    t.skipIfFloatTextureFormatNotColorRenderable(t.params.format);
     if (kTextureFormatInfo[t.params.format].color.type === 'unfilterable-float') {
       t.selectDeviceOrSkipTestCase('float32-filterable');
     }
@@ -968,6 +972,7 @@ g.test('mipmapFilter')
   )
   .beforeAllSubcases(t => {
     t.skipIfTextureFormatNotSupported(t.params.format);
+    t.skipIfFloatTextureFormatNotColorRenderable(t.params.format);
     if (kTextureFormatInfo[t.params.format].color.type === 'unfilterable-float') {
       t.selectDeviceOrSkipTestCase('float32-filterable');
     }
diff --git a/src/webgpu/format_info.ts b/src/webgpu/format_info.ts
index e65838fd2a10..8c1e9b7d7d04 100644
--- a/src/webgpu/format_info.ts
+++ b/src/webgpu/format_info.ts
@@ -1793,6 +1793,10 @@ export function canUseAsRenderTarget(format: GPUTextureFormat) {
   return kTextureFormatInfo[format].colorRender || isDepthOrStencilTextureFormat(format);
 }
 
+export function is16Float(format: GPUTextureFormat) {
+  return format === 'r16float' || format === 'rg16float' || format === 'rgba16float';
+}
+
 export function is32Float(format: GPUTextureFormat) {
   return format === 'r32float' || format === 'rg32float' || format === 'rgba32float';
 }
diff --git a/src/webgpu/gpu_test.ts b/src/webgpu/gpu_test.ts
index 4ba256669d60..867ea6533598 100644
--- a/src/webgpu/gpu_test.ts
+++ b/src/webgpu/gpu_test.ts
@@ -32,6 +32,8 @@ import {
   isCompressedTextureFormat,
   ColorTextureFormat,
   isTextureFormatUsableAsStorageFormat,
+  is32Float,
+  is16Float,
 } from './format_info.js';
 import { checkElementsEqual, checkElementsBetween } from './util/check_contents.js';
 import { CommandBufferMaker, EncoderType } from './util/command_buffer_maker.js';
@@ -279,6 +281,30 @@ export class GPUTestSubcaseBatchState extends SubcaseBatchState {
     }
   }
 
+  getFloatTextureFormatColorRenderableFeatures(...formats: (GPUTextureFormat | undefined)[]) {
+    const requiredFeatures: GPUFeatureName[] = [];
+    if (this.isCompatibility) {
+      for (const format of formats) {
+        if (format === undefined) continue;
+        if (is32Float(format)) {
+          requiredFeatures.push('float32-renderable' as GPUFeatureName);
+        } else if (is16Float(format)) {
+          requiredFeatures.push('float16-renderable' as GPUFeatureName);
+        }
+      }
+    }
+    return requiredFeatures;
+  }
+
+  /** Skips test if format is float16 or float32 and not color renderable based on device feature availability. */
+  skipIfFloatTextureFormatNotColorRenderable(...formats: (GPUTextureFormat | undefined)[]) {
+    if (this.isCompatibility) {
+      this.selectDeviceOrSkipTestCase({
+        requiredFeatures: this.getFloatTextureFormatColorRenderableFeatures(...formats),
+      });
+    }
+  }
+
   skipIfCopyTextureToTextureNotSupportedForFormat(...formats: (GPUTextureFormat | undefined)[]) {
     if (this.isCompatibility) {
       for (const format of formats) {
diff --git a/src/webgpu/web_platform/canvas/configure.spec.ts b/src/webgpu/web_platform/canvas/configure.spec.ts
index 69e058d8d1a3..6d1f9404adb9 100644
--- a/src/webgpu/web_platform/canvas/configure.spec.ts
+++ b/src/webgpu/web_platform/canvas/configure.spec.ts
@@ -179,7 +179,7 @@ g.test('format')
         format,
       });
       const configuration = ctx.getConfiguration();
-      t.expect(configuration!.format === format);
+      t.expect(configuration.format === format);
     } else {
       t.shouldThrow('TypeError', () => {
         ctx.configure({
@@ -224,7 +224,7 @@ g.test('usage')
     });
 
     const configuration = ctx.getConfiguration();
-    t.expect(configuration!.usage === usage);
+    t.expect(configuration.usage === usage);
 
     const currentTexture = ctx.getCurrentTexture();
     t.expect(currentTexture instanceof GPUTexture);
@@ -343,7 +343,7 @@ g.test('alpha_mode')
     });
 
     const configuration = ctx.getConfiguration();
-    t.expect(configuration!.alphaMode === alphaMode);
+    t.expect(configuration.alphaMode === alphaMode);
 
     const currentTexture = ctx.getCurrentTexture();
     t.expect(currentTexture instanceof GPUTexture);