From 003cb57f8ce7f037d7ac67519f64874ff903ea65 Mon Sep 17 00:00:00 2001 From: Greggman Date: Tue, 19 Nov 2024 15:30:15 -0800 Subject: [PATCH] Fix readTextureToTextureViews for multisampled textures (#4049) * Fix readTextureToTextureViews for multisampled textures I'm not sure when this bug was introduced. These tests (textureLoad:multisampled) used to pass. Fixed the bug and added a test to hopefully find it quicker if it breaks in the future. Also added a test for createTextureWithRandomDataAndGetTexels with a generator. This was fixed in a previous PR without a a specific test as the fact that it failed other tests showed it needed fixing but I decided it's probably best to keep these tests too. * Update src/webgpu/shader/execution/expression/call/builtin/texture_utils.ts Co-authored-by: Kai Ninomiya --- .../call/builtin/texture_utils.spec.ts | 48 ++++++++++++++++++- .../expression/call/builtin/texture_utils.ts | 14 ++++-- 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/src/webgpu/shader/execution/expression/call/builtin/texture_utils.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/texture_utils.spec.ts index f4be99ee65b6..caa544fac318 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/texture_utils.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/texture_utils.spec.ts @@ -4,6 +4,7 @@ Tests for texture_utils.ts import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { assert } from '../../../../../../common/util/util.js'; +import { isMultisampledTextureFormat, kDepthStencilFormats } from '../../../../../format_info.js'; import { GPUTest } from '../../../../../gpu_test.js'; import { getTextureDimensionFromView, virtualMipSize } from '../../../../../util/texture/base.js'; import { @@ -16,6 +17,7 @@ import { chooseTextureSize, createTextureWithRandomDataAndGetTexels, isSupportedViewFormatCombo, + makeRandomDepthComparisonTexelGenerator, readTextureToTexelViews, texelsApproximatelyEqual, } from './texture_utils.js'; @@ -26,6 +28,41 @@ function texelFormat(texel: Readonly>, rep: TexelRepre return rep.componentOrder.map(component => `${component}: ${texel[component]}`).join(', '); } +g.test('createTextureWithRandomDataAndGetTexels_with_generator') + .desc( + ` + Test createTextureWithRandomDataAndGetTexels with a generator. Generators + are only used with textureXXXCompare builtins as we need specific random + values to test these builtins with a depth reference value. + ` + ) + .params(u => + u + .combine('format', kDepthStencilFormats) + .combine('viewDimension', ['2d', '2d-array', 'cube', 'cube-array'] as const) + .filter(t => isSupportedViewFormatCombo(t.format, t.viewDimension)) + ) + .beforeAllSubcases(t => { + t.skipIfTextureViewDimensionNotSupported(t.params.viewDimension); + t.selectDeviceForTextureFormatOrSkipTestCase(t.params.format); + }) + .fn(async t => { + const { format, viewDimension } = t.params; + const size = chooseTextureSize({ minSize: 8, minBlocks: 4, format, viewDimension }); + const descriptor: GPUTextureDescriptor = { + format, + dimension: getTextureDimensionFromView(viewDimension), + size, + mipLevelCount: 3, + usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING, + ...(t.isCompatibility && { textureBindingViewDimension: viewDimension }), + }; + await createTextureWithRandomDataAndGetTexels(t, descriptor, { + generator: makeRandomDepthComparisonTexelGenerator(descriptor, 'equal'), + }); + // We don't expect any particular results. We just expect no validation errors. + }); + g.test('readTextureToTexelViews') .desc('test readTextureToTexelViews for various formats and dimensions') .params(u => @@ -44,19 +81,26 @@ g.test('readTextureToTexelViews') ] as const) .combine('viewDimension', ['1d', '2d', '2d-array', '3d', 'cube', 'cube-array'] as const) .filter(t => isSupportedViewFormatCombo(t.srcFormat, t.viewDimension)) + .combine('sampleCount', [1, 4] as const) + .unless( + t => + t.sampleCount > 1 && + (!isMultisampledTextureFormat(t.srcFormat) || t.viewDimension !== '2d') + ) ) .beforeAllSubcases(t => { t.skipIfTextureViewDimensionNotSupported(t.params.viewDimension); }) .fn(async t => { - const { srcFormat, texelViewFormat, viewDimension } = t.params; + const { srcFormat, texelViewFormat, viewDimension, sampleCount } = t.params; const size = chooseTextureSize({ minSize: 8, minBlocks: 4, format: srcFormat, viewDimension }); const descriptor: GPUTextureDescriptor = { format: srcFormat, dimension: getTextureDimensionFromView(viewDimension), size, - mipLevelCount: viewDimension === '1d' ? 1 : 3, + mipLevelCount: viewDimension === '1d' || sampleCount > 1 ? 1 : 3, usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING, + sampleCount, ...(t.isCompatibility && { textureBindingViewDimension: viewDimension }), }; const { texels: expectedTexelViews, texture } = await createTextureWithRandomDataAndGetTexels( 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 bae7620882c9..f3758a649399 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/texture_utils.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/texture_utils.ts @@ -3031,11 +3031,14 @@ export async function readTextureToTexelViews( } `, }); - const sampleType = isDepthTextureFormat(texture.format) - ? 'unfilterable-float' - : isStencilTextureFormat(texture.format) + const info = kTextureFormatInfo[texture.format]; + const sampleType = info.depth + ? 'unfilterable-float' // depth only supports unfilterable-float if not a comparison. + : info.stencil ? 'uint' - : kTextureFormatInfo[texture.format].color?.type ?? 'unfilterable-float'; + : info.color.type === 'float' + ? 'unfilterable-float' + : info.color.type; const bindGroupLayout = device.createBindGroupLayout({ entries: [ { @@ -3051,6 +3054,7 @@ export async function readTextureToTexelViews( texture: { sampleType, viewDimension, + multisampled: texture.sampleCount > 1, }, }, { @@ -3169,7 +3173,7 @@ function createTextureFromTexelViewsLocal( ): GPUTexture { const modifiedDescriptor = { ...desc }; // If it's a depth or stencil texture we need to render to it to fill it with data. - if (isDepthOrStencilTextureFormat(desc.format)) { + if (isDepthOrStencilTextureFormat(desc.format) || desc.sampleCount! > 1) { modifiedDescriptor.usage = desc.usage | GPUTextureUsage.RENDER_ATTACHMENT; } return createTextureFromTexelViews(t, texelViews, modifiedDescriptor);