Skip to content

Commit

Permalink
Refactor api/operations/resource_init
Browse files Browse the repository at this point in the history
  • Loading branch information
greggman committed Feb 25, 2025
1 parent c8154cf commit 723845f
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 64 deletions.
4 changes: 2 additions & 2 deletions src/webgpu/api/operation/resource_init/buffer.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { unreachable } from '../../../../common/util/util.js';
import { GPUConst } from '../../../constants.js';
import { GPUTest } from '../../../gpu_test.js';
import { AllFeaturesMaxLimitsGPUTest } from '../../../gpu_test.js';
import { getTextureCopyLayout } from '../../../util/texture/layout.js';
import { PerTexelComponent } from '../../../util/texture/texel_data.js';

Expand All @@ -21,7 +21,7 @@ const kBufferUsagesForMappedAtCreationTests = [
GPUConst.BufferUsage.COPY_SRC,
];

class F extends GPUTest {
class F extends AllFeaturesMaxLimitsGPUTest {
GetBufferUsageFromMapMode(mapMode: GPUMapModeFlags): number {
switch (mapMode) {
case GPUMapMode.READ:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { assert } from '../../../../../common/util/util.js';
import { kTextureFormatInfo, EncodableTextureFormat } from '../../../../format_info.js';
import { EncodableTextureFormat } from '../../../../format_info.js';
import { virtualMipSize } from '../../../../util/texture/base.js';

import { CheckContents } from './texture_zero_init_test.js';
Expand All @@ -12,7 +11,6 @@ export const checkContentsByBufferCopy: CheckContents = (
subresourceRange
) => {
for (const { level: mipLevel, layer } of subresourceRange.each()) {
assert(params.format in kTextureFormatInfo);
const format = params.format as EncodableTextureFormat;

t.expectSingleColor(texture, format, {
Expand All @@ -33,7 +31,6 @@ export const checkContentsByTextureCopy: CheckContents = (
subresourceRange
) => {
for (const { level, layer } of subresourceRange.each()) {
assert(params.format in kTextureFormatInfo);
const format = params.format as EncodableTextureFormat;

const [width, height, depth] = virtualMipSize(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { assert } from '../../../../../common/util/util.js';
import { kTextureFormatInfo } from '../../../../format_info.js';
import { isDepthTextureFormat, isStencilTextureFormat } from '../../../../format_info.js';
import { GPUTest } from '../../../../gpu_test.js';
import { virtualMipSize } from '../../../../util/texture/base.js';

Expand Down Expand Up @@ -106,8 +106,6 @@ const checkContents: (type: 'depth' | 'stencil', ...args: Parameters<CheckConten
state,
subresourceRange
) => {
const formatInfo = kTextureFormatInfo[params.format];

assert(params.dimension === '2d');
for (const viewDescriptor of t.generateTextureViewDescriptorsForRendering(
'all',
Expand Down Expand Up @@ -153,10 +151,10 @@ const checkContents: (type: 'depth' | 'stencil', ...args: Parameters<CheckConten
],
depthStencilAttachment: {
view: texture.createView(viewDescriptor),
depthLoadOp: formatInfo.depth ? 'load' : undefined,
depthStoreOp: formatInfo.depth ? 'store' : undefined,
stencilLoadOp: formatInfo.stencil ? 'load' : undefined,
stencilStoreOp: formatInfo.stencil ? 'store' : undefined,
depthLoadOp: isDepthTextureFormat(params.format) ? 'load' : undefined,
depthStoreOp: isDepthTextureFormat(params.format) ? 'store' : undefined,
stencilLoadOp: isStencilTextureFormat(params.format) ? 'load' : undefined,
stencilStoreOp: isStencilTextureFormat(params.format) ? 'store' : undefined,
},
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { assert, unreachable } from '../../../../../common/util/util.js';
import { kTextureFormatInfo, EncodableTextureFormat } from '../../../../format_info.js';
import { EncodableTextureFormat } from '../../../../format_info.js';
import { virtualMipSize } from '../../../../util/texture/base.js';
import {
kTexelRepresentationInfo,
Expand All @@ -16,7 +16,6 @@ export const checkContentsBySampling: CheckContents = (
state,
subresourceRange
) => {
assert(params.format in kTextureFormatInfo);
const format = params.format as EncodableTextureFormat;
const rep = kTexelRepresentationInfo[format];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,22 @@ 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,
isColorTextureFormat,
isDepthTextureFormat,
isStencilTextureFormat,
isDepthOrStencilTextureFormat,
isTextureFormatPossiblyUsableAsRenderAttachment,
isTextureFormatPossiblyStorageReadable,
isTextureFormatPossiblyMultisampled,
canCopyAllAspectsOfTextureFormat,
isTextureFormatColorRenderable,
isTextureFormatPossiblyUsableAsColorRenderAttachment,
} from '../../../../format_info.js';
import { GPUTest, GPUTestSubcaseBatchState } from '../../../../gpu_test.js';
import { AllFeaturesMaxLimitsGPUTest, 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';
Expand Down Expand Up @@ -115,14 +124,6 @@ const initializedStateAsStencil = {
[InitializedState.Canary]: 42,
};

function allAspectsCopyDst(info: (typeof kTextureFormatInfo)[UncompressedTextureFormat]) {
return (
(!info.color || info.color.copyDst) &&
(!info.depth || info.depth.copyDst) &&
(!info.stencil || info.stencil.copyDst)
);
}

export function getRequiredTextureUsage(
format: UncompressedTextureFormat,
sampleCount: number,
Expand Down Expand Up @@ -167,18 +168,22 @@ export function getRequiredTextureUsage(
usage |= GPUConst.TextureUsage.RENDER_ATTACHMENT;
}

const info = kTextureFormatInfo[format];
if (!allAspectsCopyDst(info)) {
if (!canCopyAllAspectsOfTextureFormat(format)) {
// Copies are not possible. We need OutputAttachment to initialize
// canary data.
if (info.color) assert(!!info.colorRender, 'not implemented for non-renderable color');
if (isColorTextureFormat(format)) {
assert(
isTextureFormatPossiblyUsableAsColorRenderAttachment(format),
'not implemented for non-renderable color'
);
}
usage |= GPUConst.TextureUsage.RENDER_ATTACHMENT;
}

return usage;
}

export class TextureZeroInitTest extends GPUTest {
export class TextureZeroInitTest extends AllFeaturesMaxLimitsGPUTest {
readonly stateToTexelComponents: { [k in InitializedState]: PerTexelComponent<number> };

private p: TextureZeroParams;
Expand Down Expand Up @@ -302,7 +307,7 @@ export class TextureZeroInitTest extends GPUTest {
'all',
subresourceRange
)) {
if (kTextureFormatInfo[this.p.format].color) {
if (isColorTextureFormat(this.p.format)) {
commandEncoder
.beginRenderPass({
colorAttachments: [
Expand All @@ -319,12 +324,12 @@ export class TextureZeroInitTest extends GPUTest {
const depthStencilAttachment: GPURenderPassDepthStencilAttachment = {
view: texture.createView(viewDescriptor),
};
if (kTextureFormatInfo[this.p.format].depth) {
if (isDepthTextureFormat(this.p.format)) {
depthStencilAttachment.depthClearValue = initializedStateAsDepth[state];
depthStencilAttachment.depthLoadOp = 'clear';
depthStencilAttachment.depthStoreOp = 'store';
}
if (kTextureFormatInfo[this.p.format].stencil) {
if (isStencilTextureFormat(this.p.format)) {
depthStencilAttachment.stencilClearValue = initializedStateAsStencil[state];
depthStencilAttachment.stencilLoadOp = 'clear';
depthStencilAttachment.stencilStoreOp = 'store';
Expand All @@ -347,7 +352,6 @@ export class TextureZeroInitTest extends GPUTest {
state: InitializedState,
subresourceRange: SubresourceRange
): void {
assert(this.p.format in kTextureFormatInfo);
const format = this.p.format as EncodableTextureFormat;

const firstSubresource = subresourceRange.each().next().value;
Expand Down Expand Up @@ -394,11 +398,15 @@ export class TextureZeroInitTest extends GPUTest {
state: InitializedState,
subresourceRange: SubresourceRange
): void {
const info = kTextureFormatInfo[this.p.format];
if (this.p.sampleCount > 1 || !allAspectsCopyDst(info)) {
if (this.p.sampleCount > 1 || !canCopyAllAspectsOfTextureFormat(this.p.format)) {
// Copies to multisampled textures not yet specified.
// Use a storeOp for now.
if (info.color) assert(!!info.colorRender, 'not implemented for non-renderable color');
if (isColorTextureFormat(this.p.format)) {
assert(
isTextureFormatColorRenderable(this.device, this.p.format),
'not implemented for non-renderable color'
);
}
this.initializeWithStoreOp(state, texture, subresourceRange);
} else {
this.initializeWithCopy(texture, state, subresourceRange);
Expand All @@ -410,7 +418,7 @@ export class TextureZeroInitTest extends GPUTest {
commandEncoder.pushDebugGroup('discardTexture');

for (const desc of this.generateTextureViewDescriptorsForRendering('all', subresourceRange)) {
if (kTextureFormatInfo[this.p.format].color) {
if (isColorTextureFormat(this.p.format)) {
commandEncoder
.beginRenderPass({
colorAttachments: [
Expand All @@ -426,11 +434,11 @@ export class TextureZeroInitTest extends GPUTest {
const depthStencilAttachment: GPURenderPassDepthStencilAttachment = {
view: texture.createView(desc),
};
if (kTextureFormatInfo[this.p.format].depth) {
if (isDepthTextureFormat(this.p.format)) {
depthStencilAttachment.depthLoadOp = 'load';
depthStencilAttachment.depthStoreOp = 'discard';
}
if (kTextureFormatInfo[this.p.format].stencil) {
if (isStencilTextureFormat(this.p.format)) {
depthStencilAttachment.stencilLoadOp = 'load';
depthStencilAttachment.stencilStoreOp = 'discard';
}
Expand All @@ -446,6 +454,19 @@ export class TextureZeroInitTest extends GPUTest {
commandEncoder.popDebugGroup();
this.queue.submit([commandEncoder.finish()]);
}

skipIfTextureFormatNotSupportedForTest(params: TextureZeroParams) {
const { format, sampleCount, uninitializeMethod, readMethod } = params;
this.skipIfTextureFormatNotSupported(format);

const usage = getRequiredTextureUsage(format, sampleCount, uninitializeMethod, readMethod);

this.skipIfTextureFormatDoesNotSupportUsage(usage, format);

if (sampleCount > 1) {
this.skipIfTextureFormatNotMultisampled(format);
}
}
}

export const kTestParams = kUnitCaseParamsBuilder
Expand All @@ -463,16 +484,18 @@ export const kTestParams = kUnitCaseParamsBuilder
.beginSubcases()
.combine('aspect', kTextureAspects)
.unless(({ readMethod, format, aspect }) => {
const info = kTextureFormatInfo[format];
const hasColor = isColorTextureFormat(format);
const hasDepth = isDepthTextureFormat(format);
const hasStencil = isStencilTextureFormat(format);
return (
(readMethod === ReadMethod.DepthTest && (!info.depth || aspect === 'stencil-only')) ||
(readMethod === ReadMethod.StencilTest && (!info.stencil || aspect === 'depth-only')) ||
(readMethod === ReadMethod.ColorBlending && !info.color) ||
(readMethod === ReadMethod.DepthTest && (!hasDepth || aspect === 'stencil-only')) ||
(readMethod === ReadMethod.StencilTest && (!hasStencil || aspect === 'depth-only')) ||
(readMethod === ReadMethod.ColorBlending && !hasColor) ||
// [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) ||
(readMethod === ReadMethod.Sample && (hasDepth || hasStencil)) ||
(aspect === 'depth-only' && !hasDepth) ||
(aspect === 'stencil-only' && !hasStencil) ||
(aspect === 'all' && !!hasDepth && !!hasStencil) ||
// Cannot copy from a packed depth format.
// [2]: Test copying out of the stencil aspect.
((readMethod === ReadMethod.CopyToBuffer || readMethod === ReadMethod.CopyToTexture) &&
Expand All @@ -493,12 +516,10 @@ export const kTestParams = kUnitCaseParamsBuilder
.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 ||
isDepthOrStencilTextureFormat(format) ||
readMethod === ReadMethod.DepthTest ||
readMethod === ReadMethod.StencilTest ||
readMethod === ReadMethod.ColorBlending ||
Expand All @@ -521,14 +542,13 @@ export const kTestParams = kUnitCaseParamsBuilder
.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.color &&
!info.colorRender) ||
((usage & GPUConst.TextureUsage.STORAGE_BINDING) !== 0 && !info.color?.storage) ||
(sampleCount > 1 && !info.multisample)
!isTextureFormatPossiblyUsableAsRenderAttachment(format)) ||
((usage & GPUConst.TextureUsage.STORAGE_BINDING) !== 0 &&
!isTextureFormatPossiblyStorageReadable(format)) ||
(sampleCount > 1 && !isTextureFormatPossiblyMultisampled(format))
);
})
.combine('nonPowerOfTwo', [false, true])
Expand Down
10 changes: 1 addition & 9 deletions src/webgpu/api/operation/resource_init/texture_zero.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ TODO:

import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { unreachable } from '../../../../common/util/util.js';
import { isMultisampledTextureFormatDeprecated, kTextureFormatInfo } from '../../../format_info.js';

import { checkContentsByBufferCopy, checkContentsByTextureCopy } from './check_texture/by_copy.js';
import {
Expand Down Expand Up @@ -41,15 +40,8 @@ export const g = makeTestGroup(TextureZeroInitTest);

g.test('uninitialized_texture_is_zero')
.params(kTestParams)
.beforeAllSubcases(t => {
t.skipIfTextureFormatNotSupportedDeprecated(t.params.format);
t.selectDeviceOrSkipTestCase(kTextureFormatInfo[t.params.format].feature);
})
.fn(t => {
t.skipIf(
t.params.sampleCount > 1 &&
!isMultisampledTextureFormatDeprecated(t.params.format, t.isCompatibility)
);
t.skipIfTextureFormatNotSupportedForTest(t.params);

const usage = getRequiredTextureUsage(
t.params.format,
Expand Down
13 changes: 13 additions & 0 deletions src/webgpu/format_info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1894,10 +1894,23 @@ export function filterFormatsByFeature<T>(
return formats.filter(f => f === undefined || kTextureFormatInfo[f].feature === feature);
}

export function canCopyAllAspectsOfTextureFormat(format: GPUTextureFormat) {
const info = kTextureFormatInfo[format];
return (
(!info.color || info.color.copyDst) &&
(!info.depth || info.depth.copyDst) &&
(!info.stencil || info.stencil.copyDst)
);
}

export function isCompressedTextureFormat(format: GPUTextureFormat) {
return format in kCompressedTextureFormatInfo;
}

export function isColorTextureFormat(format: GPUTextureFormat) {
return !!kTextureFormatInfo[format].color;
}

export function isDepthTextureFormat(format: GPUTextureFormat) {
return !!kTextureFormatInfo[format].depth;
}
Expand Down

0 comments on commit 723845f

Please sign in to comment.