summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/resource_init/texture_zero.spec.ts
diff options
context:
space:
mode:
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/resource_init/texture_zero.spec.ts')
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/resource_init/texture_zero.spec.ts554
1 files changed, 11 insertions, 543 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/resource_init/texture_zero.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/resource_init/texture_zero.spec.ts
index 3f0baeccbd..ee4c141aef 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/resource_init/texture_zero.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/resource_init/texture_zero.spec.ts
@@ -7,550 +7,9 @@ TODO:
- test compressed texture formats [3]
`;
-// MAINTENANCE_TODO: This is a test file, it probably shouldn't export anything.
-// Everything that's exported should be moved to another file.
-
-import { TestCaseRecorder, TestParams } from '../../../../common/framework/fixture.js';
-import {
- kUnitCaseParamsBuilder,
- ParamTypeOf,
-} from '../../../../common/framework/params_builder.js';
import { makeTestGroup } from '../../../../common/framework/test_group.js';
-import { assert, unreachable } from '../../../../common/util/util.js';
-import { kTextureAspects, kTextureDimensions } from '../../../capability_info.js';
-import { GPUConst } from '../../../constants.js';
-import {
- kTextureFormatInfo,
- kUncompressedTextureFormats,
- textureDimensionAndFormatCompatible,
- UncompressedTextureFormat,
- EncodableTextureFormat,
-} from '../../../format_info.js';
-import { GPUTest, GPUTestSubcaseBatchState } from '../../../gpu_test.js';
-import { virtualMipSize } from '../../../util/texture/base.js';
-import { createTextureUploadBuffer } from '../../../util/texture/layout.js';
-import { BeginEndRange, SubresourceRange } from '../../../util/texture/subresource.js';
-import { PerTexelComponent, kTexelRepresentationInfo } from '../../../util/texture/texel_data.js';
-
-export enum UninitializeMethod {
- Creation = 'Creation', // The texture was just created. It is uninitialized.
- StoreOpClear = 'StoreOpClear', // The texture was rendered to with GPUStoreOp "clear"
-}
-const kUninitializeMethods = Object.keys(UninitializeMethod) as UninitializeMethod[];
-
-export const enum ReadMethod {
- Sample = 'Sample', // The texture is sampled from
- CopyToBuffer = 'CopyToBuffer', // The texture is copied to a buffer
- CopyToTexture = 'CopyToTexture', // The texture is copied to another texture
- DepthTest = 'DepthTest', // The texture is read as a depth buffer
- StencilTest = 'StencilTest', // The texture is read as a stencil buffer
- ColorBlending = 'ColorBlending', // Read the texture by blending as a color attachment
- Storage = 'Storage', // Read the texture as a storage texture
-}
-
-// Test with these mip level counts
-type MipLevels = 1 | 5;
-const kMipLevelCounts: MipLevels[] = [1, 5];
-
-// For each mip level count, define the mip ranges to leave uninitialized.
-const kUninitializedMipRangesToTest: { [k in MipLevels]: BeginEndRange[] } = {
- 1: [{ begin: 0, end: 1 }], // Test the only mip
- 5: [
- { begin: 0, end: 2 },
- { begin: 3, end: 4 },
- ], // Test a range and a single mip
-};
-
-// Test with these sample counts.
-const kSampleCounts: number[] = [1, 4];
-
-// Test with these layer counts.
-type LayerCounts = 1 | 7;
-
-// For each layer count, define the layers to leave uninitialized.
-const kUninitializedLayerRangesToTest: { [k in LayerCounts]: BeginEndRange[] } = {
- 1: [{ begin: 0, end: 1 }], // Test the only layer
- 7: [
- { begin: 2, end: 4 },
- { begin: 6, end: 7 },
- ], // Test a range and a single layer
-};
-
-// Enums to abstract over color / depth / stencil values in textures. Depending on the texture format,
-// the data for each value may have a different representation. These enums are converted to a
-// representation such that their values can be compared. ex.) An integer is needed to upload to an
-// unsigned normalized format, but its value is read as a float in the shader.
-export const enum InitializedState {
- Canary, // Set on initialized subresources. It should stay the same. On discarded resources, we should observe zero.
- Zero, // We check that uninitialized subresources are in this state when read back.
-}
-
-const initializedStateAsFloat = {
- [InitializedState.Zero]: 0,
- [InitializedState.Canary]: 1,
-};
-
-const initializedStateAsUint = {
- [InitializedState.Zero]: 0,
- [InitializedState.Canary]: 1,
-};
-
-const initializedStateAsSint = {
- [InitializedState.Zero]: 0,
- [InitializedState.Canary]: -1,
-};
-
-function initializedStateAsColor(
- state: InitializedState,
- format: GPUTextureFormat
-): [number, number, number, number] {
- let value;
- if (format.indexOf('uint') !== -1) {
- value = initializedStateAsUint[state];
- } else if (format.indexOf('sint') !== -1) {
- value = initializedStateAsSint[state];
- } else {
- value = initializedStateAsFloat[state];
- }
- return [value, value, value, value];
-}
-
-const initializedStateAsDepth = {
- [InitializedState.Zero]: 0,
- [InitializedState.Canary]: 0.8,
-};
-
-const initializedStateAsStencil = {
- [InitializedState.Zero]: 0,
- [InitializedState.Canary]: 42,
-};
-
-function getRequiredTextureUsage(
- format: UncompressedTextureFormat,
- sampleCount: number,
- uninitializeMethod: UninitializeMethod,
- readMethod: ReadMethod
-): GPUTextureUsageFlags {
- let usage: GPUTextureUsageFlags = GPUConst.TextureUsage.COPY_DST;
-
- switch (uninitializeMethod) {
- case UninitializeMethod.Creation:
- break;
- case UninitializeMethod.StoreOpClear:
- usage |= GPUConst.TextureUsage.RENDER_ATTACHMENT;
- break;
- default:
- unreachable();
- }
-
- switch (readMethod) {
- case ReadMethod.CopyToBuffer:
- case ReadMethod.CopyToTexture:
- usage |= GPUConst.TextureUsage.COPY_SRC;
- break;
- case ReadMethod.Sample:
- usage |= GPUConst.TextureUsage.TEXTURE_BINDING;
- break;
- case ReadMethod.Storage:
- usage |= GPUConst.TextureUsage.STORAGE_BINDING;
- break;
- case ReadMethod.DepthTest:
- case ReadMethod.StencilTest:
- case ReadMethod.ColorBlending:
- usage |= GPUConst.TextureUsage.RENDER_ATTACHMENT;
- break;
- default:
- unreachable();
- }
-
- if (sampleCount > 1) {
- // Copies to multisampled textures are not allowed. We need OutputAttachment to initialize
- // canary data in multisampled textures.
- usage |= GPUConst.TextureUsage.RENDER_ATTACHMENT;
- }
-
- if (!kTextureFormatInfo[format].copyDst) {
- // Copies are not possible. We need OutputAttachment to initialize
- // canary data.
- assert(kTextureFormatInfo[format].renderable);
- usage |= GPUConst.TextureUsage.RENDER_ATTACHMENT;
- }
-
- return usage;
-}
-
-export class TextureZeroInitTest extends GPUTest {
- readonly stateToTexelComponents: { [k in InitializedState]: PerTexelComponent<number> };
-
- private p: TextureZeroParams;
- constructor(sharedState: GPUTestSubcaseBatchState, rec: TestCaseRecorder, params: TestParams) {
- super(sharedState, rec, params);
- this.p = params as TextureZeroParams;
-
- const stateToTexelComponents = (state: InitializedState) => {
- const [R, G, B, A] = initializedStateAsColor(state, this.p.format);
- return {
- R,
- G,
- B,
- A,
- Depth: initializedStateAsDepth[state],
- Stencil: initializedStateAsStencil[state],
- };
- };
-
- this.stateToTexelComponents = {
- [InitializedState.Zero]: stateToTexelComponents(InitializedState.Zero),
- [InitializedState.Canary]: stateToTexelComponents(InitializedState.Canary),
- };
- }
-
- get textureWidth(): number {
- let width = 1 << this.p.mipLevelCount;
- if (this.p.nonPowerOfTwo) {
- width = 2 * width - 1;
- }
- return width;
- }
-
- get textureHeight(): number {
- if (this.p.dimension === '1d') {
- return 1;
- }
-
- let height = 1 << this.p.mipLevelCount;
- if (this.p.nonPowerOfTwo) {
- height = 2 * height - 1;
- }
- return height;
- }
-
- get textureDepth(): number {
- return this.p.dimension === '3d' ? 11 : 1;
- }
-
- get textureDepthOrArrayLayers(): number {
- return this.p.dimension === '2d' ? this.p.layerCount : this.textureDepth;
- }
-
- // Used to iterate subresources and check that their uninitialized contents are zero when accessed
- *iterateUninitializedSubresources(): Generator<SubresourceRange> {
- for (const mipRange of kUninitializedMipRangesToTest[this.p.mipLevelCount]) {
- for (const layerRange of kUninitializedLayerRangesToTest[this.p.layerCount]) {
- yield new SubresourceRange({ mipRange, layerRange });
- }
- }
- }
-
- // Used to iterate and initialize other subresources not checked for zero-initialization.
- // Zero-initialization of uninitialized subresources should not have side effects on already
- // initialized subresources.
- *iterateInitializedSubresources(): Generator<SubresourceRange> {
- const uninitialized: boolean[][] = new Array(this.p.mipLevelCount);
- for (let level = 0; level < uninitialized.length; ++level) {
- uninitialized[level] = new Array(this.p.layerCount);
- }
- for (const subresources of this.iterateUninitializedSubresources()) {
- for (const { level, layer } of subresources.each()) {
- uninitialized[level][layer] = true;
- }
- }
- for (let level = 0; level < uninitialized.length; ++level) {
- for (let layer = 0; layer < uninitialized[level].length; ++layer) {
- if (!uninitialized[level][layer]) {
- yield new SubresourceRange({
- mipRange: { begin: level, count: 1 },
- layerRange: { begin: layer, count: 1 },
- });
- }
- }
- }
- }
-
- *generateTextureViewDescriptorsForRendering(
- aspect: GPUTextureAspect,
- subresourceRange?: SubresourceRange
- ): Generator<GPUTextureViewDescriptor> {
- const viewDescriptor: GPUTextureViewDescriptor = {
- dimension: '2d',
- aspect,
- };
-
- if (subresourceRange === undefined) {
- return viewDescriptor;
- }
-
- for (const { level, layer } of subresourceRange.each()) {
- yield {
- ...viewDescriptor,
- baseMipLevel: level,
- mipLevelCount: 1,
- baseArrayLayer: layer,
- arrayLayerCount: 1,
- };
- }
- }
-
- private initializeWithStoreOp(
- state: InitializedState,
- texture: GPUTexture,
- subresourceRange?: SubresourceRange
- ): void {
- const commandEncoder = this.device.createCommandEncoder();
- commandEncoder.pushDebugGroup('initializeWithStoreOp');
-
- for (const viewDescriptor of this.generateTextureViewDescriptorsForRendering(
- 'all',
- subresourceRange
- )) {
- if (kTextureFormatInfo[this.p.format].color) {
- commandEncoder
- .beginRenderPass({
- colorAttachments: [
- {
- view: texture.createView(viewDescriptor),
- storeOp: 'store',
- clearValue: initializedStateAsColor(state, this.p.format),
- loadOp: 'clear',
- },
- ],
- })
- .end();
- } else {
- const depthStencilAttachment: GPURenderPassDepthStencilAttachment = {
- view: texture.createView(viewDescriptor),
- };
- if (kTextureFormatInfo[this.p.format].depth) {
- depthStencilAttachment.depthClearValue = initializedStateAsDepth[state];
- depthStencilAttachment.depthLoadOp = 'clear';
- depthStencilAttachment.depthStoreOp = 'store';
- }
- if (kTextureFormatInfo[this.p.format].stencil) {
- depthStencilAttachment.stencilClearValue = initializedStateAsStencil[state];
- depthStencilAttachment.stencilLoadOp = 'clear';
- depthStencilAttachment.stencilStoreOp = 'store';
- }
- commandEncoder
- .beginRenderPass({
- colorAttachments: [],
- depthStencilAttachment,
- })
- .end();
- }
- }
-
- commandEncoder.popDebugGroup();
- this.queue.submit([commandEncoder.finish()]);
- }
-
- private initializeWithCopy(
- texture: GPUTexture,
- state: InitializedState,
- subresourceRange: SubresourceRange
- ): void {
- assert(this.p.format in kTextureFormatInfo);
- const format = this.p.format as EncodableTextureFormat;
-
- const firstSubresource = subresourceRange.each().next().value;
- assert(typeof firstSubresource !== 'undefined');
-
- const [largestWidth, largestHeight, largestDepth] = virtualMipSize(
- this.p.dimension,
- [this.textureWidth, this.textureHeight, this.textureDepth],
- firstSubresource.level
- );
-
- const rep = kTexelRepresentationInfo[format];
- const texelData = new Uint8Array(rep.pack(rep.encode(this.stateToTexelComponents[state])));
- const { buffer, bytesPerRow, rowsPerImage } = createTextureUploadBuffer(
- texelData,
- this.device,
- format,
- this.p.dimension,
- [largestWidth, largestHeight, largestDepth]
- );
-
- const commandEncoder = this.device.createCommandEncoder();
-
- for (const { level, layer } of subresourceRange.each()) {
- const [width, height, depth] = virtualMipSize(
- this.p.dimension,
- [this.textureWidth, this.textureHeight, this.textureDepth],
- level
- );
-
- commandEncoder.copyBufferToTexture(
- {
- buffer,
- bytesPerRow,
- rowsPerImage,
- },
- { texture, mipLevel: level, origin: { x: 0, y: 0, z: layer } },
- { width, height, depthOrArrayLayers: depth }
- );
- }
- this.queue.submit([commandEncoder.finish()]);
- buffer.destroy();
- }
-
- initializeTexture(
- texture: GPUTexture,
- state: InitializedState,
- subresourceRange: SubresourceRange
- ): void {
- if (this.p.sampleCount > 1 || !kTextureFormatInfo[this.p.format].copyDst) {
- // Copies to multisampled textures not yet specified.
- // Use a storeOp for now.
- assert(kTextureFormatInfo[this.p.format].renderable);
- this.initializeWithStoreOp(state, texture, subresourceRange);
- } else {
- this.initializeWithCopy(texture, state, subresourceRange);
- }
- }
-
- discardTexture(texture: GPUTexture, subresourceRange: SubresourceRange): void {
- const commandEncoder = this.device.createCommandEncoder();
- commandEncoder.pushDebugGroup('discardTexture');
-
- for (const desc of this.generateTextureViewDescriptorsForRendering('all', subresourceRange)) {
- if (kTextureFormatInfo[this.p.format].color) {
- commandEncoder
- .beginRenderPass({
- colorAttachments: [
- {
- view: texture.createView(desc),
- storeOp: 'discard',
- loadOp: 'load',
- },
- ],
- })
- .end();
- } else {
- const depthStencilAttachment: GPURenderPassDepthStencilAttachment = {
- view: texture.createView(desc),
- };
- if (kTextureFormatInfo[this.p.format].depth) {
- depthStencilAttachment.depthLoadOp = 'load';
- depthStencilAttachment.depthStoreOp = 'discard';
- }
- if (kTextureFormatInfo[this.p.format].stencil) {
- depthStencilAttachment.stencilLoadOp = 'load';
- depthStencilAttachment.stencilStoreOp = 'discard';
- }
- commandEncoder
- .beginRenderPass({
- colorAttachments: [],
- depthStencilAttachment,
- })
- .end();
- }
- }
-
- commandEncoder.popDebugGroup();
- this.queue.submit([commandEncoder.finish()]);
- }
-}
-
-const kTestParams = kUnitCaseParamsBuilder
- .combine('dimension', kTextureDimensions)
- .combine('readMethod', [
- ReadMethod.CopyToBuffer,
- ReadMethod.CopyToTexture,
- ReadMethod.Sample,
- ReadMethod.DepthTest,
- ReadMethod.StencilTest,
- ])
- // [3] compressed formats
- .combine('format', kUncompressedTextureFormats)
- .filter(({ dimension, format }) => textureDimensionAndFormatCompatible(dimension, format))
- .beginSubcases()
- .combine('aspect', kTextureAspects)
- .unless(({ readMethod, format, aspect }) => {
- const info = kTextureFormatInfo[format];
- return (
- (readMethod === ReadMethod.DepthTest && (!info.depth || aspect === 'stencil-only')) ||
- (readMethod === ReadMethod.StencilTest && (!info.stencil || aspect === 'depth-only')) ||
- (readMethod === ReadMethod.ColorBlending && !info.color) ||
- // [1]: Test with depth/stencil sampling
- (readMethod === ReadMethod.Sample && (!!info.depth || !!info.stencil)) ||
- (aspect === 'depth-only' && !info.depth) ||
- (aspect === 'stencil-only' && !info.stencil) ||
- (aspect === 'all' && !!info.depth && !!info.stencil) ||
- // Cannot copy from a packed depth format.
- // [2]: Test copying out of the stencil aspect.
- ((readMethod === ReadMethod.CopyToBuffer || readMethod === ReadMethod.CopyToTexture) &&
- (format === 'depth24plus' || format === 'depth24plus-stencil8'))
- );
- })
- .combine('mipLevelCount', kMipLevelCounts)
- // 1D texture can only have a single mip level
- .unless(p => p.dimension === '1d' && p.mipLevelCount !== 1)
- .combine('sampleCount', kSampleCounts)
- .unless(
- ({ readMethod, sampleCount }) =>
- // We can only read from multisampled textures by sampling.
- sampleCount > 1 &&
- (readMethod === ReadMethod.CopyToBuffer || readMethod === ReadMethod.CopyToTexture)
- )
- // Multisampled textures may only have one mip
- .unless(({ sampleCount, mipLevelCount }) => sampleCount > 1 && mipLevelCount > 1)
- .combine('uninitializeMethod', kUninitializeMethods)
- .unless(({ dimension, readMethod, uninitializeMethod, format, sampleCount }) => {
- const formatInfo = kTextureFormatInfo[format];
- return (
- dimension !== '2d' &&
- (sampleCount > 1 ||
- !!formatInfo.depth ||
- !!formatInfo.stencil ||
- readMethod === ReadMethod.DepthTest ||
- readMethod === ReadMethod.StencilTest ||
- readMethod === ReadMethod.ColorBlending ||
- uninitializeMethod === UninitializeMethod.StoreOpClear)
- );
- })
- .expandWithParams(function* ({ dimension }) {
- switch (dimension) {
- case '2d':
- yield { layerCount: 1 as LayerCounts };
- yield { layerCount: 7 as LayerCounts };
- break;
- case '1d':
- case '3d':
- yield { layerCount: 1 as LayerCounts };
- break;
- }
- })
- // Multisampled 3D / 2D array textures not supported.
- .unless(({ sampleCount, layerCount }) => sampleCount > 1 && layerCount > 1)
- .unless(({ format, sampleCount, uninitializeMethod, readMethod }) => {
- const usage = getRequiredTextureUsage(format, sampleCount, uninitializeMethod, readMethod);
- const info = kTextureFormatInfo[format];
-
- return (
- ((usage & GPUConst.TextureUsage.RENDER_ATTACHMENT) !== 0 && !info.renderable) ||
- ((usage & GPUConst.TextureUsage.STORAGE_BINDING) !== 0 && !info.color?.storage) ||
- (sampleCount > 1 && !info.multisample)
- );
- })
- .combine('nonPowerOfTwo', [false, true])
- .combine('canaryOnCreation', [false, true])
- .filter(({ canaryOnCreation, format }) => {
- // We can only initialize the texture if it's encodable or renderable.
- const canInitialize = format in kTextureFormatInfo || kTextureFormatInfo[format].renderable;
-
- // Filter out cases where we want canary values but can't initialize.
- return !canaryOnCreation || canInitialize;
- });
-
-type TextureZeroParams = ParamTypeOf<typeof kTestParams>;
-
-export type CheckContents = (
- t: TextureZeroInitTest,
- params: TextureZeroParams,
- texture: GPUTexture,
- state: InitializedState,
- subresourceRange: SubresourceRange
-) => void;
+import { unreachable } from '../../../../common/util/util.js';
+import { kTextureFormatInfo } from '../../../format_info.js';
import { checkContentsByBufferCopy, checkContentsByTextureCopy } from './check_texture/by_copy.js';
import {
@@ -558,6 +17,15 @@ import {
checkContentsByStencilTest,
} from './check_texture/by_ds_test.js';
import { checkContentsBySampling } from './check_texture/by_sampling.js';
+import {
+ getRequiredTextureUsage,
+ ReadMethod,
+ CheckContents,
+ TextureZeroInitTest,
+ kTestParams,
+ UninitializeMethod,
+ InitializedState,
+} from './check_texture/texture_zero_init_test.js';
const checkContentsImpl: { [k in ReadMethod]: CheckContents } = {
Sample: checkContentsBySampling,