From c30b7f84de37df078aa2ce2454f32178563a801a Mon Sep 17 00:00:00 2001 From: ShrekShao <5031596+shrekshao@users.noreply.github.com> Date: Tue, 21 Jan 2025 17:39:52 -0800 Subject: [PATCH] Compat: fix float16(32)-renderable tests (validation) (#4152) * Compat: fix float16(32)-renderable tests (validation) * address review comments * address review comments 2 * fix * fix2 --- .../maxColorAttachmentBytesPerSample.spec.ts | 8 +- .../api/validation/createBindGroup.spec.ts | 18 ++++- .../api/validation/createTexture.spec.ts | 6 ++ src/webgpu/api/validation/createView.spec.ts | 3 + .../createRenderBundleEncoder.spec.ts | 5 ++ .../CopyExternalImageToTexture.spec.ts | 1 + .../attachment_compatibility.spec.ts | 4 +- .../render_pass_descriptor.spec.ts | 9 ++- .../render_pipeline/float32_blendable.spec.ts | 1 + .../render_pipeline/fragment_state.spec.ts | 5 ++ src/webgpu/capability_info.ts | 25 ++++-- src/webgpu/gpu_test.ts | 81 +++++++++++++++++++ 12 files changed, 148 insertions(+), 18 deletions(-) diff --git a/src/webgpu/api/validation/capability_checks/limits/maxColorAttachmentBytesPerSample.spec.ts b/src/webgpu/api/validation/capability_checks/limits/maxColorAttachmentBytesPerSample.spec.ts index b9e78e102876..5fc21daf5aae 100644 --- a/src/webgpu/api/validation/capability_checks/limits/maxColorAttachmentBytesPerSample.spec.ts +++ b/src/webgpu/api/validation/capability_checks/limits/maxColorAttachmentBytesPerSample.spec.ts @@ -11,16 +11,16 @@ import { } from './limit_utils.js'; const kFormatsToUseBySize: GPUTextureFormat[] = [ - 'rgba32float', - 'rgba16float', + 'rgba32uint', + 'rgba16uint', 'rgba8unorm', 'rg8unorm', 'r8unorm', ]; const kInterleaveFormats: GPUTextureFormat[] = [ - 'rgba16float', - 'rg16float', + 'rgba16uint', + 'rg16uint', 'rgba8unorm', 'rg8unorm', 'r8unorm', diff --git a/src/webgpu/api/validation/createBindGroup.spec.ts b/src/webgpu/api/validation/createBindGroup.spec.ts index 23b1354d7387..ecbd118240d7 100644 --- a/src/webgpu/api/validation/createBindGroup.spec.ts +++ b/src/webgpu/api/validation/createBindGroup.spec.ts @@ -30,6 +30,8 @@ import { getTextureDimensionFromView } from '../../util/texture/base.js'; import { ValidationTest } from './validation_test.js'; +const kTestFormat: GPUTextureFormat = 'r32float'; + function clone(descriptor: T): T { return JSON.parse(JSON.stringify(descriptor)); } @@ -144,7 +146,7 @@ g.test('binding_must_contain_resource_defined_in_layout') .params(u => u // .combine('resourceType', kBindableResources) - .combine('entry', allBindingEntries(false)) + .combine('entry', allBindingEntries(false, kTestFormat)) ) .fn(t => { const { resourceType, entry } = t.params; @@ -195,7 +197,7 @@ g.test('texture_binding_must_have_correct_usage') .desc('Tests that texture bindings must have the correct usage.') .paramsSubcasesOnly(u => u // - .combine('entry', sampledAndStorageBindingEntries(false)) + .combine('entry', sampledAndStorageBindingEntries(false, kTestFormat)) .combine('usage', kTextureUsages) .unless(({ entry, usage }) => { const info = texBindingTypeInfo(entry); @@ -203,10 +205,18 @@ g.test('texture_binding_must_have_correct_usage') return usage === GPUConst.TextureUsage.STORAGE_BINDING && info.resource === 'sampledTexMS'; }) ) + .beforeAllSubcases(t => { + t.selectDeviceForRenderableColorFormatOrSkipTestCase(kTestFormat); + }) .fn(t => { const { entry, usage } = t.params; const info = texBindingTypeInfo(entry); + t.skipIf( + t.isCompatibility && info.resource === 'sampledTexMS', + "The test requires 'r32float' multisampled support which compat mode doesn't guarantee." + ); + const bindGroupLayout = t.device.createBindGroupLayout({ entries: [{ binding: 0, visibility: GPUShaderStage.COMPUTE, ...entry }], }); @@ -217,7 +227,7 @@ g.test('texture_binding_must_have_correct_usage') const descriptor = { size: { width: 16, height: 16, depthOrArrayLayers: 1 }, - format: 'r32float' as const, + format: kTestFormat, usage: appliedUsage, sampleCount: info.resource === 'sampledTexMS' ? 4 : 1, }; @@ -601,7 +611,7 @@ g.test('texture,resource_state') .paramsSubcasesOnly(u => u .combine('state', kResourceStates) - .combine('entry', sampledAndStorageBindingEntries(true)) + .combine('entry', sampledAndStorageBindingEntries(true, kTestFormat)) .combine('visibilityMask', [kAllShaderStages, GPUConst.ShaderStage.COMPUTE] as const) ) .fn(t => { diff --git a/src/webgpu/api/validation/createTexture.spec.ts b/src/webgpu/api/validation/createTexture.spec.ts index 3de91245f19c..2a1a0c91939f 100644 --- a/src/webgpu/api/validation/createTexture.spec.ts +++ b/src/webgpu/api/validation/createTexture.spec.ts @@ -355,11 +355,16 @@ g.test('sampleCount,valid_sampleCount_with_other_parameter_varies') const info = kTextureFormatInfo[format]; t.skipIfTextureFormatNotSupported(format); t.selectDeviceOrSkipTestCase(info.feature); + t.selectDeviceForRenderableColorFormatOrSkipTestCase(format); }) .fn(t => { const { dimension, sampleCount, format, mipLevelCount, arrayLayerCount, usage } = t.params; const { blockWidth, blockHeight } = kTextureFormatInfo[format]; + if (sampleCount > 1) { + t.skipIfMultisampleNotSupportedForFormat(format); + } + const size = dimension === '1d' ? [32 * blockWidth, 1 * blockHeight, 1] @@ -1047,6 +1052,7 @@ g.test('texture_usage') const info = kTextureFormatInfo[format]; t.skipIfTextureFormatNotSupported(format); t.selectDeviceOrSkipTestCase(info.feature); + t.selectDeviceForRenderableColorFormatOrSkipTestCase(format); }) .fn(t => { const { dimension, format, usage0, usage1 } = t.params; diff --git a/src/webgpu/api/validation/createView.spec.ts b/src/webgpu/api/validation/createView.spec.ts index c3e56bb4f011..7f6490128339 100644 --- a/src/webgpu/api/validation/createView.spec.ts +++ b/src/webgpu/api/validation/createView.spec.ts @@ -378,6 +378,9 @@ g.test('texture_view_usage') if (textureUsage & GPUTextureUsage.STORAGE_BINDING) { t.skipIfTextureFormatNotUsableAsStorageTexture(format); } + if (textureUsage & GPUTextureUsage.RENDER_ATTACHMENT) { + t.selectDeviceForRenderableColorFormatOrSkipTestCase(format); + } }) .fn(t => { const { format, textureUsage0, textureUsage1, textureViewUsage0, textureViewUsage1 } = t.params; diff --git a/src/webgpu/api/validation/encoding/createRenderBundleEncoder.spec.ts b/src/webgpu/api/validation/encoding/createRenderBundleEncoder.spec.ts index ade00417411b..4167ba6db31a 100644 --- a/src/webgpu/api/validation/encoding/createRenderBundleEncoder.spec.ts +++ b/src/webgpu/api/validation/encoding/createRenderBundleEncoder.spec.ts @@ -63,6 +63,7 @@ g.test('attachment_state,limits,maxColorAttachmentBytesPerSample,aligned') ) .beforeAllSubcases(t => { t.skipIfTextureFormatNotSupported(t.params.format); + t.selectDeviceForRenderableColorFormatOrSkipTestCase(t.params.format); }) .fn(t => { const { format, colorFormatCount } = t.params; @@ -118,6 +119,9 @@ g.test('attachment_state,limits,maxColorAttachmentBytesPerSample,unaligned') }, ]) ) + .beforeAllSubcases(t => { + t.selectDeviceForRenderableColorFormatOrSkipTestCase('r32float'); + }) .fn(t => { const { formats } = t.params; @@ -168,6 +172,7 @@ g.test('valid_texture_formats') .beforeAllSubcases(t => { const { format } = t.params; t.selectDeviceForTextureFormatOrSkipTestCase(format); + t.selectDeviceForRenderableColorFormatOrSkipTestCase(format); }) .fn(t => { const { format, attachment } = t.params; diff --git a/src/webgpu/api/validation/queue/copyToTexture/CopyExternalImageToTexture.spec.ts b/src/webgpu/api/validation/queue/copyToTexture/CopyExternalImageToTexture.spec.ts index 622133721bed..d0082a6abef6 100644 --- a/src/webgpu/api/validation/queue/copyToTexture/CopyExternalImageToTexture.spec.ts +++ b/src/webgpu/api/validation/queue/copyToTexture/CopyExternalImageToTexture.spec.ts @@ -682,6 +682,7 @@ g.test('destination_texture,format') const { format } = t.params; t.skipIfTextureFormatNotSupported(format); t.selectDeviceOrSkipTestCase(kTextureFormatInfo[format].feature); + t.selectDeviceForRenderableColorFormatOrSkipTestCase(format); }) .fn(async t => { const { format, copySize } = t.params; 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 8f5982f8af18..02132c584c3e 100644 --- a/src/webgpu/api/validation/render_pass/attachment_compatibility.spec.ts +++ b/src/webgpu/api/validation/render_pass/attachment_compatibility.spec.ts @@ -182,6 +182,7 @@ g.test('render_pass_and_bundle,color_format') const { passFormat, bundleFormat } = t.params; t.skipIfTextureFormatNotSupported(passFormat, bundleFormat); + t.skipIfColorRenderableNotSupportedForFormat(passFormat, bundleFormat); const bundleEncoder = t.device.createRenderBundleEncoder({ colorFormats: [bundleFormat], @@ -358,7 +359,7 @@ g.test('render_pass_and_bundle,device_mismatch') const { mismatched } = t.params; const sourceDevice = mismatched ? t.mismatchedDevice : t.device; - const format = 'r16float'; + const format = 'r16uint'; const bundleEncoder = sourceDevice.createRenderBundleEncoder({ colorFormats: [format], }); @@ -390,6 +391,7 @@ Test that color attachment formats in render passes or bundles match the pipelin const { encoderType, encoderFormat, pipelineFormat } = t.params; t.skipIfTextureFormatNotSupported(encoderFormat, pipelineFormat); + t.skipIfColorRenderableNotSupportedForFormat(encoderFormat, pipelineFormat); const pipeline = t.createRenderPipeline([{ format: pipelineFormat, writeMask: 0 }]); 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 3ded92c5c022..3a563d70d97a 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 @@ -211,6 +211,7 @@ g.test('color_attachments,limits,maxColorAttachmentBytesPerSample,aligned') ) .beforeAllSubcases(t => { t.skipIfTextureFormatNotSupported(t.params.format); + t.selectDeviceForRenderableColorFormatOrSkipTestCase(t.params.format); }) .fn(t => { const { format, attachmentCount } = t.params; @@ -267,6 +268,9 @@ g.test('color_attachments,limits,maxColorAttachmentBytesPerSample,unaligned') }, ]) ) + .beforeAllSubcases(t => { + t.selectDeviceForRenderableColorFormatOrSkipTestCase('r32float'); + }) .fn(t => { const { formats } = t.params; @@ -1168,7 +1172,10 @@ g.test('resolveTarget,format_supports_resolve') .filter(t => kTextureFormatInfo[t.format].multisample) ) .beforeAllSubcases(t => { - t.skipIfTextureFormatNotSupported(t.params.format); + const { format } = t.params; + t.skipIfTextureFormatNotSupported(format); + t.skipIfMultisampleNotSupportedForFormat(format); + t.selectDeviceForRenderableColorFormatOrSkipTestCase(format); }) .fn(t => { const { format } = t.params; diff --git a/src/webgpu/api/validation/render_pipeline/float32_blendable.spec.ts b/src/webgpu/api/validation/render_pipeline/float32_blendable.spec.ts index ed387b5a8769..a6ea65a137bd 100644 --- a/src/webgpu/api/validation/render_pipeline/float32_blendable.spec.ts +++ b/src/webgpu/api/validation/render_pipeline/float32_blendable.spec.ts @@ -30,6 +30,7 @@ pipeline that uses blending with any float32-format attachment. if (t.params.enabled) { t.selectDeviceOrSkipTestCase('float32-blendable'); } + t.selectDeviceForRenderableColorFormatOrSkipTestCase('r32float'); }) .fn(t => { const { isAsync, enabled, hasBlend, format } = t.params; 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 efbe8b0b5b94..3e39b6a0591f 100644 --- a/src/webgpu/api/validation/render_pipeline/fragment_state.spec.ts +++ b/src/webgpu/api/validation/render_pipeline/fragment_state.spec.ts @@ -181,6 +181,7 @@ g.test('limits,maxColorAttachmentBytesPerSample,aligned') ) .beforeAllSubcases(t => { t.skipIfTextureFormatNotSupported(t.params.format); + t.selectDeviceForRenderableColorFormatOrSkipTestCase(t.params.format); }) .fn(t => { const { format, attachmentCount, isAsync } = t.params; @@ -228,6 +229,9 @@ g.test('limits,maxColorAttachmentBytesPerSample,unaligned') .beginSubcases() .combine('isAsync', [false, true]) ) + .beforeAllSubcases(t => { + t.selectDeviceForRenderableColorFormatOrSkipTestCase('r32float'); + }) .fn(t => { const { formats, isAsync } = t.params; @@ -394,6 +398,7 @@ g.test('pipeline_output_targets') ) .beforeAllSubcases(t => { t.selectDeviceForTextureFormatOrSkipTestCase(t.params.format); + t.selectDeviceForRenderableColorFormatOrSkipTestCase(t.params.format); }) .fn(t => { const { isAsync, format, writeMask, shaderOutput } = t.params; diff --git a/src/webgpu/capability_info.ts b/src/webgpu/capability_info.ts index 4da99bad66e8..5b464a7c1d60 100644 --- a/src/webgpu/capability_info.ts +++ b/src/webgpu/capability_info.ts @@ -592,16 +592,22 @@ export function textureBindingEntries(includeUndefined: boolean): readonly BGLEn * * Note: Generates different `access` options, but not `format` or `viewDimension` options. */ -export function storageTextureBindingEntries(): readonly BGLEntry[] { +export function storageTextureBindingEntries(format: GPUTextureFormat): readonly BGLEntry[] { return [ - { storageTexture: { access: 'write-only', format: 'r32float' } }, - { storageTexture: { access: 'read-only', format: 'r32float' } }, - { storageTexture: { access: 'read-write', format: 'r32float' } }, + { storageTexture: { access: 'write-only', format } }, + { storageTexture: { access: 'read-only', format } }, + { storageTexture: { access: 'read-write', format } }, ] as const; } /** Generate a list of possible texture-or-storageTexture-typed BGLEntry values. */ -export function sampledAndStorageBindingEntries(includeUndefined: boolean): readonly BGLEntry[] { - return [...textureBindingEntries(includeUndefined), ...storageTextureBindingEntries()] as const; +export function sampledAndStorageBindingEntries( + includeUndefined: boolean, + format: GPUTextureFormat = 'r32float' +): readonly BGLEntry[] { + return [ + ...textureBindingEntries(includeUndefined), + ...storageTextureBindingEntries(format), + ] as const; } /** * Generate a list of possible BGLEntry values of every type, but not variants with different: @@ -610,11 +616,14 @@ export function sampledAndStorageBindingEntries(includeUndefined: boolean): read * - texture.viewDimension * - storageTexture.viewDimension */ -export function allBindingEntries(includeUndefined: boolean): readonly BGLEntry[] { +export function allBindingEntries( + includeUndefined: boolean, + format: GPUTextureFormat = 'r32float' +): readonly BGLEntry[] { return [ ...bufferBindingEntries(includeUndefined), ...samplerBindingEntries(includeUndefined), - ...sampledAndStorageBindingEntries(includeUndefined), + ...sampledAndStorageBindingEntries(includeUndefined, format), ] as const; } diff --git a/src/webgpu/gpu_test.ts b/src/webgpu/gpu_test.ts index 27a5b40321fe..149d57f85ba3 100644 --- a/src/webgpu/gpu_test.ts +++ b/src/webgpu/gpu_test.ts @@ -34,6 +34,7 @@ import { isTextureFormatUsableAsStorageFormat, is32Float, is16Float, + isMultisampledTextureFormat, } from './format_info.js'; import { checkElementsEqual, checkElementsBetween } from './util/check_contents.js'; import { CommandBufferMaker, EncoderType } from './util/command_buffer_maker.js'; @@ -288,6 +289,46 @@ export class GPUTestSubcaseBatchState extends SubcaseBatchState { } } + skipIfMultisampleNotSupportedForFormat(...formats: (GPUTextureFormat | undefined)[]) { + if (this.isCompatibility) { + for (const format of formats) { + if (format === undefined) continue; + if (format === 'rgba16float' || is32Float(format)) { + this.skip( + `texture format '${format} is not guaranteed to be multisampled support in compat mode` + ); + } + } + } + + for (const format of formats) { + if (format === undefined) continue; + if (!isMultisampledTextureFormat(format)) { + this.skip(`texture format '${format} is not supported to be multisampled`); + } + } + } + + skipIfColorRenderableNotSupportedForFormat(...formats: (GPUTextureFormat | undefined)[]) { + if (this.isCompatibility) { + for (const format of formats) { + if (format === undefined) continue; + if (is16Float(format) || is32Float(format)) { + this.skip( + `texture format '${format} is not guaranteed to be color renderable in compat mode` + ); + } + } + } + + for (const format of formats) { + if (format === undefined) continue; + if (!kTextureFormatInfo[format].color) { + this.skip(`texture format '${format} is not color renderable`); + } + } + } + getFloatTextureFormatColorRenderableFeatures(...formats: (GPUTextureFormat | undefined)[]) { const requiredFeatures: GPUFeatureName[] = []; if (this.isCompatibility) { @@ -543,6 +584,46 @@ export class GPUTestBase extends Fixture { } } + skipIfMultisampleNotSupportedForFormat(...formats: (GPUTextureFormat | undefined)[]) { + if (this.isCompatibility) { + for (const format of formats) { + if (format === undefined) continue; + if (format === 'rgba16float' || is32Float(format)) { + this.skip( + `texture format '${format} is not guaranteed to be multisampled support in compat mode` + ); + } + } + } + + for (const format of formats) { + if (format === undefined) continue; + if (!isMultisampledTextureFormat(format)) { + this.skip(`texture format '${format} is not supported to be multisampled`); + } + } + } + + skipIfColorRenderableNotSupportedForFormat(...formats: (GPUTextureFormat | undefined)[]) { + if (this.isCompatibility) { + for (const format of formats) { + if (format === undefined) continue; + if (is16Float(format) || is32Float(format)) { + this.skip( + `texture format '${format} is not guaranteed to be color renderable in compat mode` + ); + } + } + } + + for (const format of formats) { + if (format === undefined) continue; + if (!kTextureFormatInfo[format].color) { + this.skip(`texture format '${format} is not color renderable`); + } + } + } + skipIfTextureViewDimensionNotSupported(...dimensions: (GPUTextureViewDimension | undefined)[]) { if (this.isCompatibility) { for (const dimension of dimensions) {