diff --git a/src/webgpu/shader/execution/expression/call/builtin/textureGather.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/textureGather.spec.ts index d2ba15adb96..f8fdd61d8ba 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/textureGather.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/textureGather.spec.ts @@ -28,9 +28,8 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { isDepthTextureFormat, isEncodableTextureFormat, - kCompressedTextureFormats, kDepthStencilFormats, - kEncodableTextureFormats, + kAllTextureFormats, } from '../../../../../format_info.js'; import { @@ -54,8 +53,6 @@ import { WGSLTextureSampleTest, } from './texture_utils.js'; -const kTestableColorFormats = [...kEncodableTextureFormats, ...kCompressedTextureFormats] as const; - export const g = makeTestGroup(WGSLTextureSampleTest); g.test('sampled_2d_coords') @@ -87,7 +84,7 @@ Parameters: .params(u => u .combine('stage', kShortShaderStages) - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => isFillable(t.format)) .combine('filt', ['nearest', 'linear'] as const) .combine('modeU', kShortAddressModes) @@ -184,7 +181,7 @@ Parameters: .params(u => u .combine('stage', kShortShaderStages) - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => isFillable(t.format)) .combine('filt', ['nearest', 'linear'] as const) .combine('mode', kShortAddressModes) @@ -292,7 +289,7 @@ Parameters: .params(u => u .combine('stage', kShortShaderStages) - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => isFillable(t.format)) .combine('filt', ['nearest', 'linear'] as const) .combine('modeU', kShortAddressModes) @@ -398,7 +395,7 @@ Parameters: .params(u => u .combine('stage', kShortShaderStages) - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => isFillable(t.format)) .combine('filt', ['nearest', 'linear'] as const) .combine('mode', kShortAddressModes) diff --git a/src/webgpu/shader/execution/expression/call/builtin/textureLoad.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/textureLoad.spec.ts index 689df4feb08..37107f2eb4b 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/textureLoad.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/textureLoad.spec.ts @@ -29,9 +29,8 @@ import { isEncodableTextureFormat, isMultisampledTextureFormat, isStencilTextureFormat, - kCompressedTextureFormats, kDepthStencilFormats, - kEncodableTextureFormats, + kAllTextureFormats, kTextureFormatInfo, textureDimensionAndFormatCompatible, } from '../../../../../format_info.js'; @@ -58,8 +57,6 @@ import { createVideoFrameWithRandomDataAndGetTexels, } from './texture_utils.js'; -const kTestableColorFormats = [...kEncodableTextureFormats, ...kCompressedTextureFormats] as const; - export function normalizedCoordToTexelLoadTestCoord( descriptor: GPUTextureDescriptor, mipLevel: number, @@ -92,7 +89,7 @@ Parameters: .params(u => u .combine('stage', kShortShaderStages) - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => textureDimensionAndFormatCompatible('1d', t.format)) // 1d textures can't have a height !== 1 .filter(t => kTextureFormatInfo[t.format].blockHeight === 1) @@ -179,7 +176,7 @@ Parameters: .params(u => u .combine('stage', kShortShaderStages) - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => !isCompressedFloatTextureFormat(t.format)) .beginSubcases() .combine('samplePoints', kSamplePointMethods) @@ -261,7 +258,7 @@ Parameters: .params(u => u .combine('stage', kShortShaderStages) - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => textureDimensionAndFormatCompatible('3d', t.format)) .beginSubcases() .combine('samplePoints', kSamplePointMethods) @@ -351,7 +348,7 @@ Parameters: 'texture_multisampled_2d', 'texture_depth_multisampled_2d', ] as const) - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => isMultisampledTextureFormat(t.format)) .filter(t => !isStencilTextureFormat(t.format)) // Filter out texture_depth_multisampled_2d with non-depth formats @@ -604,7 +601,7 @@ Parameters: .params(u => u .combine('stage', kShortShaderStages) - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) // MAINTENANCE_TODO: Update createTextureFromTexelViews to support stencil8 and remove this filter. .filter(t => t.format !== 'stencil8' && !isCompressedFloatTextureFormat(t.format)) .combine('texture_type', ['texture_2d_array', 'texture_depth_2d_array'] as const) diff --git a/src/webgpu/shader/execution/expression/call/builtin/textureSample.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/textureSample.spec.ts index 36492276a4b..5fcb95b9c05 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/textureSample.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/textureSample.spec.ts @@ -11,9 +11,8 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { isDepthTextureFormat, isEncodableTextureFormat, - kCompressedTextureFormats, kDepthStencilFormats, - kEncodableTextureFormats, + kAllTextureFormats, textureDimensionAndFormatCompatible, } from '../../../../../format_info.js'; import { TextureTestMixin } from '../../../../../gpu_test.js'; @@ -44,8 +43,6 @@ import { skipIfNeedsFilteringAndIsUnfilterable, } from './texture_utils.js'; -const kTestableColorFormats = [...kEncodableTextureFormats, ...kCompressedTextureFormats] as const; - export const g = makeTestGroup(TextureTestMixin(WGSLTextureSampleTest)); g.test('sampled_1d_coords') @@ -62,7 +59,7 @@ Parameters: ) .params(u => u - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => textureDimensionAndFormatCompatible('1d', t.format)) .filter(t => isPotentiallyFilterableAndFillable(t.format)) .combine('filt', ['nearest', 'linear'] as const) @@ -151,7 +148,7 @@ Parameters: ) .params(u => u - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => isPotentiallyFilterableAndFillable(t.format)) .combine('filt', ['nearest', 'linear'] as const) .combine('modeU', kShortAddressModes) @@ -249,7 +246,7 @@ Parameters: ) .params(u => u - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => isPotentiallyFilterableAndFillable(t.format)) .combine('dim', ['3d', 'cube'] as const) .filter(t => isSupportedViewFormatCombo(t.format, t.dim)) @@ -478,7 +475,7 @@ Parameters: ) .params(u => u - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => isPotentiallyFilterableAndFillable(t.format)) .combine('filt', ['nearest', 'linear'] as const) .combine('modeU', kShortAddressModes) @@ -574,7 +571,7 @@ Parameters: ) .params(u => u - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => isPotentiallyFilterableAndFillable(t.format)) .combine('filt', ['nearest', 'linear'] as const) .combine('mode', kShortAddressModes) diff --git a/src/webgpu/shader/execution/expression/call/builtin/textureSampleBias.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/textureSampleBias.spec.ts index f49322f878d..21446dc7eea 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/textureSampleBias.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/textureSampleBias.spec.ts @@ -8,7 +8,7 @@ Samples a texture with a bias to the mip level. `; import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; -import { kCompressedTextureFormats, kEncodableTextureFormats } from '../../../../../format_info.js'; +import { kAllTextureFormats } from '../../../../../format_info.js'; import { TextureTestMixin } from '../../../../../gpu_test.js'; import { @@ -35,8 +35,6 @@ import { skipIfNeedsFilteringAndIsUnfilterable, } from './texture_utils.js'; -const kTestableColorFormats = [...kEncodableTextureFormats, ...kCompressedTextureFormats] as const; - export const g = makeTestGroup(TextureTestMixin(WGSLTextureSampleTest)); g.test('sampled_2d_coords') @@ -61,7 +59,7 @@ Parameters: ) .params(u => u - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => isPotentiallyFilterableAndFillable(t.format)) .combine('filt', ['nearest', 'linear'] as const) .combine('modeU', kShortAddressModes) @@ -159,7 +157,7 @@ Parameters: ) .params(u => u - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => isPotentiallyFilterableAndFillable(t.format)) .combine('dim', ['3d', 'cube'] as const) .filter(t => isSupportedViewFormatCombo(t.format, t.dim)) @@ -296,7 +294,7 @@ Parameters: ) .params(u => u - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => isPotentiallyFilterableAndFillable(t.format)) .combine('filt', ['nearest', 'linear'] as const) .combine('modeU', kShortAddressModes) @@ -400,7 +398,7 @@ Parameters: ) .params(u => u - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => isPotentiallyFilterableAndFillable(t.format)) .combine('filt', ['nearest', 'linear'] as const) .combine('mode', kShortAddressModes) diff --git a/src/webgpu/shader/execution/expression/call/builtin/textureSampleGrad.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/textureSampleGrad.spec.ts index 8da6ffdfe9c..d018abc3329 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/textureSampleGrad.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/textureSampleGrad.spec.ts @@ -6,7 +6,7 @@ Samples a texture using explicit gradients. `; import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; -import { kCompressedTextureFormats, kEncodableTextureFormats } from '../../../../../format_info.js'; +import { kAllTextureFormats } from '../../../../../format_info.js'; import { appendComponentTypeForFormatToTextureType, @@ -34,8 +34,6 @@ import { WGSLTextureSampleTest, } from './texture_utils.js'; -const kTestableColorFormats = [...kEncodableTextureFormats, ...kCompressedTextureFormats] as const; - export const g = makeTestGroup(WGSLTextureSampleTest); g.test('sampled_2d_coords') @@ -62,7 +60,7 @@ Parameters: .params(u => u .combine('stage', kShortShaderStages) - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => isPotentiallyFilterableAndFillable(t.format)) .combine('filt', ['nearest', 'linear'] as const) .combine('modeU', kShortAddressModes) @@ -161,7 +159,7 @@ Parameters: .params(u => u .combine('stage', kShortShaderStages) - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => isPotentiallyFilterableAndFillable(t.format)) .combine('dim', ['3d', 'cube'] as const) .filter(t => isSupportedViewFormatCombo(t.format, t.dim)) @@ -301,7 +299,7 @@ Parameters: .params(u => u .combine('stage', kShortShaderStages) - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => isPotentiallyFilterableAndFillable(t.format)) .combine('filt', ['nearest', 'linear'] as const) .combine('modeU', kShortAddressModes) @@ -407,7 +405,7 @@ Parameters: .params(u => u .combine('stage', kShortShaderStages) - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => isPotentiallyFilterableAndFillable(t.format)) .combine('filt', ['nearest', 'linear'] as const) .combine('mode', kShortAddressModes) diff --git a/src/webgpu/shader/execution/expression/call/builtin/textureSampleLevel.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/textureSampleLevel.spec.ts index 840bafcab22..dd054e25f35 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/textureSampleLevel.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/textureSampleLevel.spec.ts @@ -8,9 +8,8 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { isDepthTextureFormat, isEncodableTextureFormat, - kCompressedTextureFormats, + kAllTextureFormats, kDepthStencilFormats, - kEncodableTextureFormats, } from '../../../../../format_info.js'; import { @@ -40,8 +39,6 @@ import { WGSLTextureSampleTest, } from './texture_utils.js'; -const kTestableColorFormats = [...kEncodableTextureFormats, ...kCompressedTextureFormats] as const; - export const g = makeTestGroup(WGSLTextureSampleTest); g.test('sampled_2d_coords') @@ -71,7 +68,7 @@ Parameters: .params(u => u .combine('stage', kShortShaderStages) - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => isPotentiallyFilterableAndFillable(t.format)) .combine('filt', ['nearest', 'linear'] as const) .combine('modeU', kShortAddressModes) @@ -175,7 +172,7 @@ Parameters: .params(u => u .combine('stage', kShortShaderStages) - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => isPotentiallyFilterableAndFillable(t.format)) .combine('filt', ['nearest', 'linear'] as const) .combine('modeU', kShortAddressModes) @@ -283,7 +280,7 @@ Parameters: .params(u => u .combine('stage', kShortShaderStages) - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => isPotentiallyFilterableAndFillable(t.format)) .combine('dim', ['3d', 'cube'] as const) .filter(t => isSupportedViewFormatCombo(t.format, t.dim)) @@ -416,7 +413,7 @@ Parameters: .params(u => u .combine('stage', kShortShaderStages) - .combine('format', kTestableColorFormats) + .combine('format', kAllTextureFormats) .filter(t => isPotentiallyFilterableAndFillable(t.format)) .combine('filt', ['nearest', 'linear'] as const) .combine('mode', kShortAddressModes) 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 b01f3a5e758..bd9e648b747 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/texture_utils.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/texture_utils.ts @@ -109,6 +109,11 @@ export function getTextureTypeForTextureViewDimension(viewDimension: GPUTextureV const is32Float = (format: GPUTextureFormat) => format === 'r32float' || format === 'rg32float' || format === 'rgba32float'; +const isUnencodableDepthFormat = (format: GPUTextureFormat) => + format === 'depth24plus' || + format === 'depth24plus-stencil8' || + format === 'depth32float-stencil8'; + /** * Skips a subcase if the filter === 'linear' and the format is type * 'unfilterable-float' and we cannot enable filtering. @@ -1982,7 +1987,15 @@ function isValidOutOfBoundsValue( for (let sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex) { const texel = mipTexels.color({ x, y, z, sampleIndex }); const rgba = convertPerTexelComponentToResultFormat(texel, mipTexels.format); - if (texelsApproximatelyEqual(gotRGBA, rgba, mipTexels.format, maxFractionalDiff)) { + if ( + texelsApproximatelyEqual( + gotRGBA, + texture.descriptor.format, + rgba, + mipTexels.format, + maxFractionalDiff + ) + ) { return true; } } @@ -2026,23 +2039,24 @@ const kRComponent = [TexelComponent.R] as const; function texelsApproximatelyEqual( gotRGBA: PerTexelComponent, + gotFormat: GPUTextureFormat, expectRGBA: PerTexelComponent, - format: EncodableTextureFormat, + expectedFormat: EncodableTextureFormat, maxFractionalDiff: number ) { - const rep = kTexelRepresentationInfo[format]; - const got = convertResultFormatToTexelViewFormat(gotRGBA, format); - const expect = convertResultFormatToTexelViewFormat(expectRGBA, format); + const rep = kTexelRepresentationInfo[expectedFormat]; + const got = convertResultFormatToTexelViewFormat(gotRGBA, expectedFormat); + const expect = convertResultFormatToTexelViewFormat(expectRGBA, expectedFormat); const gULP = convertPerTexelComponentToResultFormat( rep.bitsToULPFromZero(rep.numberToBits(got)), - format + expectedFormat ); const eULP = convertPerTexelComponentToResultFormat( rep.bitsToULPFromZero(rep.numberToBits(expect)), - format + expectedFormat ); - const rgbaComponentsToCheck = isDepthOrStencilTextureFormat(format) + const rgbaComponentsToCheck = isDepthOrStencilTextureFormat(gotFormat) ? kRComponent : kRGBAComponents; @@ -2152,7 +2166,15 @@ export async function checkCallResults( continue; } - if (texelsApproximatelyEqual(gotRGBA, expectRGBA, format, maxFractionalDiff)) { + if ( + texelsApproximatelyEqual( + gotRGBA, + texture.descriptor.format, + expectRGBA, + format, + maxFractionalDiff + ) + ) { continue; } @@ -2360,7 +2382,15 @@ export async function checkCallResults( call, gpuTexels, async (texels: TexelView[]) => { - const gpuTexture = createTextureFromTexelViewsLocal(t, texels, texture.descriptor); + // We're trying to create a texture with black and white texels + // but if it's a compressed texture we use an encodable texture. + // It's not perfect but we already know it failed. We're just hoping + // to get sample points. + const descriptor = { ...texture.descriptor }; + if (isCompressedTextureFormat(descriptor.format)) { + descriptor.format = texels[0].format; + } + const gpuTexture = createTextureFromTexelViewsLocal(t, texels, descriptor); const result = (await checkInfo.runner.run(gpuTexture))[callIdx]; gpuTexture.destroy(); return result; @@ -2704,11 +2734,18 @@ export async function readTextureToTexelViews( const sampler = device.createSampler(); + const aspect = getAspectForTexture(texture); const bindGroup = device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [ { binding: 0, resource: { buffer: uniformBuffer } }, - { binding: 1, resource: texture.createView({ dimension: viewDimension }) }, + { + binding: 1, + resource: texture.createView({ + dimension: viewDimension, + aspect, + }), + }, { binding: 2, resource: sampler }, { binding: 3, resource: { buffer: storageBuffer } }, ], @@ -2756,7 +2793,7 @@ export async function readTextureToTexelViews( function createTextureFromTexelViewsLocal( t: GPUTest, texelViews: TexelView[], - desc: Omit + desc: GPUTextureDescriptor ): GPUTexture { const modifiedDescriptor = { ...desc }; // If it's a depth or stencil texture we need to render to it to fill it with data. @@ -2794,6 +2831,25 @@ export async function createTextureWithRandomDataAndGetTexels( getTexelViewFormatForTextureFormat(texture.format) ); return { texture, texels }; + } else if (isUnencodableDepthFormat(descriptor.format)) { + // This is round about. We can't directly write to depth24plus, depth24plus-stencil8, depth32float-stencil8 + // and they are not encodable. So: (1) we make random data using `depth32float`. We create a texture with + // that data (createTextureFromTexelViewsLocal will render the data into the texture rather than copy). + // We then need to read it back out but as rgba32float since that is encodable but, since it round tripped + // through the GPU it's now been quantized. + const d32Descriptor = { + ...descriptor, + format: 'depth32float' as GPUTextureFormat, + }; + const tempTexels = createRandomTexelViewMipmap(d32Descriptor, options); + const texture = createTextureFromTexelViewsLocal(t, tempTexels, descriptor); + const texels = await readTextureToTexelViews( + t, + texture, + descriptor, + getTexelViewFormatForTextureFormat(texture.format) + ); + return { texture, texels }; } else { const texels = createRandomTexelViewMipmap(descriptor, options); const texture = createTextureFromTexelViewsLocal(t, texels, descriptor); @@ -4263,6 +4319,15 @@ function describeTextureCall(call: TextureCall): st return `${call.builtin}(${args.join(', ')})`; } +const getAspectForTexture = (texture: GPUTexture | GPUExternalTexture): GPUTextureAspect => + texture instanceof GPUExternalTexture + ? 'all' + : isDepthTextureFormat(texture.format) + ? 'depth-only' + : isStencilTextureFormat(texture.format) + ? 'stencil-only' + : 'all'; + const s_deviceToPipelines = new WeakMap< GPUDevice, Map @@ -4642,6 +4707,12 @@ ${stageWGSL} usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ, }); + const aspect = getAspectForTexture(gpuTexture); + const runViewDescriptor = { + ...viewDescriptor, + aspect, + }; + const bindGroup0 = t.device.createBindGroup({ layout: pipeline!.getBindGroupLayout(0), entries: [ @@ -4650,7 +4721,7 @@ ${stageWGSL} resource: gpuTexture instanceof GPUExternalTexture ? gpuTexture - : gpuTexture.createView(viewDescriptor), + : gpuTexture.createView(runViewDescriptor), }, ...(sampler ? [{ binding: 1, resource: gpuSampler! }] : []), { binding: 2, resource: { buffer: dataBuffer } }, diff --git a/src/webgpu/util/texture.ts b/src/webgpu/util/texture.ts index 20e99fdfad4..745190d3331 100644 --- a/src/webgpu/util/texture.ts +++ b/src/webgpu/util/texture.ts @@ -319,24 +319,26 @@ function copyBufferToTextureViaRender( t: GPUTest, encoder: GPUCommandEncoder, source: GPUImageCopyBuffer, + sourceFormat: GPUTextureFormat, dest: GPUImageCopyTexture, size: GPUExtent3D ) { - const { format, sampleCount } = dest.texture; + const { format: textureFormat, sampleCount } = dest.texture; const origin = reifyOrigin3D(dest.origin ?? [0]); const copySize = reifyExtent3D(size); - const msInfo = kLoadValueFromStorageInfo[format]; + const msInfo = kLoadValueFromStorageInfo[sourceFormat]; assert(!!msInfo); const { useFragDepth, discardWithStencil } = msInfo; const { device } = t; const numBlits = discardWithStencil ? 8 : 1; for (let blitCount = 0; blitCount < numBlits; ++blitCount) { - const code = getCopyBufferToTextureViaRenderCode(format); + const code = getCopyBufferToTextureViaRenderCode(sourceFormat); const stencilWriteMask = 1 << blitCount; const id = JSON.stringify({ - format, + textureFormat, + sourceFormat, useFragDepth, stencilWriteMask, discardWithStencil, @@ -350,7 +352,7 @@ function copyBufferToTextureViaRender( if (!pipeline) { const module = device.createShaderModule({ code }); pipeline = device.createRenderPipeline({ - label: `blitCopyFor-${format}`, + label: `blitCopyFor-${textureFormat}`, layout: 'auto', vertex: { module }, ...(discardWithStencil @@ -362,7 +364,7 @@ function copyBufferToTextureViaRender( depthStencil: { depthWriteEnabled: false, depthCompare: 'always', - format, + format: textureFormat, stencilWriteMask, stencilFront: { passOp: 'replace', @@ -378,13 +380,13 @@ function copyBufferToTextureViaRender( depthStencil: { depthWriteEnabled: true, depthCompare: 'always', - format, + format: textureFormat, }, } : { fragment: { module, - targets: [{ format }], + targets: [{ format: textureFormat }], }, }), primitive: { @@ -395,7 +397,7 @@ function copyBufferToTextureViaRender( pipelines.set(id, pipeline); } - const info = kTextureFormatInfo[format]; + const info = kTextureFormatInfo[sourceFormat]; const uniforms = new Uint32Array([ copySize.height, // numTexelRows: u32, source.bytesPerRow!, // bytesPerRow: u32, @@ -445,6 +447,7 @@ function copyBufferToTextureViaRender( depthClearValue: 0, depthLoadOp: 'clear', depthStoreOp: 'store', + stencilReadOnly: true, }, } : { @@ -490,17 +493,19 @@ function copyBufferToTextureViaRender( export function createTextureFromTexelViews( t: GPUTest, texelViews: TexelView[], - desc: Omit + desc: Omit & { format?: GPUTextureFormat } ): GPUTexture { // All texel views must be the same format for mipmaps. assert(texelViews.length > 0 && texelViews.every(e => e.format === texelViews[0].format)); - const format = texelViews[0].format; + const viewsFormat = texelViews[0].format; + const textureFormat = desc.format ?? viewsFormat; + const isTextureFormatDifferentThanTexelViewFormat = textureFormat !== viewsFormat; const { width, height, depthOrArrayLayers } = reifyExtent3D(desc.size); // Create the texture and then initialize each mipmap level separately. const texture = t.createTextureTracked({ ...desc, - format, + format: textureFormat, usage: desc.usage | GPUTextureUsage.COPY_DST, mipLevelCount: texelViews.length, }); @@ -513,9 +518,14 @@ export function createTextureFromTexelViews( bytesPerRow, rowsPerImage, mipSize: [mipWidth, mipHeight, mipDepthOrArray], - } = getTextureCopyLayout(format, desc.dimension ?? '2d', [width, height, depthOrArrayLayers], { - mipLevel, - }); + } = getTextureCopyLayout( + viewsFormat, + desc.dimension ?? '2d', + [width, height, depthOrArrayLayers], + { + mipLevel, + } + ); // Create a staging buffer to upload the texture mip level contents. const stagingBuffer = t.createBufferTracked({ @@ -535,11 +545,16 @@ export function createTextureFromTexelViews( }); stagingBuffer.unmap(); - if (texture.sampleCount > 1 || isDepthOrStencilTextureFormat(format)) { + if ( + isTextureFormatDifferentThanTexelViewFormat || + texture.sampleCount > 1 || + isDepthOrStencilTextureFormat(textureFormat) + ) { copyBufferToTextureViaRender( t, commandEncoder, { buffer: stagingBuffer, bytesPerRow, rowsPerImage }, + viewsFormat, { texture, mipLevel }, [mipWidth, mipHeight, mipDepthOrArray] );