Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,24 @@ import { DataArrayGenerator } from '../../../util/texture/data_generation.js';
import { kBytesPerRowAlignment, dataBytesForCopyOrFail } from '../../../util/texture/layout.js';
import { TexelView } from '../../../util/texture/texel_view.js';
import { findFailedPixels } from '../../../util/texture/texture_ok.js';
import { reifyExtent3D } from '../../../util/unions.js';

const dataGenerator = new DataArrayGenerator();

// If a texture could be textureBindingViewDimension: 'cube' then set it to 'cube'
function applyTextureBindingViewDimensionForTest(descriptor: GPUTextureDescriptor) {
const size = reifyExtent3D(descriptor.size);
if (
descriptor.textureBindingViewDimension === undefined &&
descriptor.dimension === '2d' &&
size.width === size.height &&
size.depthOrArrayLayers === 6
) {
descriptor.textureBindingViewDimension = 'cube';
}
return descriptor;
}

class F extends AllFeaturesMaxLimitsGPUTest {
getInitialDataPerMipLevel(
dimension: GPUTextureDimension,
Expand Down Expand Up @@ -95,21 +110,21 @@ class F extends AllFeaturesMaxLimitsGPUTest {
const mipLevelCount = dimension === '1d' ? 1 : 4;

// Create srcTexture and dstTexture
const srcTextureDesc: GPUTextureDescriptor = {
const srcTextureDesc: GPUTextureDescriptor = applyTextureBindingViewDimensionForTest({
dimension,
size: srcTextureSize,
format: srcFormat,
usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.COPY_DST,
mipLevelCount,
};
});
const srcTexture = this.createTextureTracked(srcTextureDesc);
const dstTextureDesc: GPUTextureDescriptor = {
const dstTextureDesc: GPUTextureDescriptor = applyTextureBindingViewDimensionForTest({
dimension,
size: dstTextureSize,
format: dstFormat,
usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.COPY_DST | extraTextureUsageFlags,
mipLevelCount,
};
});
const dstTexture = this.createTextureTracked(dstTextureDesc);

// Fill the whole subresource of srcTexture at srcCopyLevel with initialSrcData.
Expand Down Expand Up @@ -999,8 +1014,12 @@ g.test('color_textures,non_compressed,array')
srcTextureSize: { width: 31, height: 32, depthOrArrayLayers: 33 },
dstTextureSize: { width: 31, height: 32, depthOrArrayLayers: 33 },
},
// Maybe used with textureBindingViewDimension: 'cube'
{
srcTextureSize: { width: 32, height: 32, depthOrArrayLayers: 6 },
dstTextureSize: { width: 32, height: 32, depthOrArrayLayers: 6 },
},
])

.combine('copyBoxOffsets', kCopyBoxOffsetsFor2DArrayTextures)
.combine('srcCopyLevel', [0, 3])
.combine('dstCopyLevel', [0, 3])
Expand Down
57 changes: 46 additions & 11 deletions src/webgpu/api/operation/command_buffer/image_copy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ Note: more coverage of memory synchronization for different read and write textu
- Convert the float32 values in initialData into the ones compatible to the depth aspect of
depthFormats when depth16unorm is supported by the browsers in
DoCopyTextureToBufferWithDepthAspectTest().
- add more cases for textureBindingViewDimension: 'cube'
- test compressed textures
- test more cases in general - refactor tests generate 'cube' compatible texture sizes.

TODO: Expand tests of GPUExtent3D [1]
`;
Expand Down Expand Up @@ -76,6 +79,7 @@ import {
} from '../../../util/texture/layout.js';
import { TexelView } from '../../../util/texture/texel_view.js';
import { findFailedPixels } from '../../../util/texture/texture_ok.js';
import { reifyExtent3D } from '../../../util/unions.js';

interface TextureCopyViewWithRequiredOrigin {
texture: GPUTexture;
Expand Down Expand Up @@ -127,6 +131,21 @@ const kMethodsToTest = [
const dataGenerator = new DataArrayGenerator();
const altDataGenerator = new DataArrayGenerator();

// If a texture could be textureBindingViewDimension: 'cube' then set it to 'cube'
function applyTextureBindingViewDimensionForTest(descriptor: GPUTextureDescriptor) {
const size = reifyExtent3D(descriptor.size);
if (
descriptor.textureBindingViewDimension === undefined &&
descriptor.dimension === '2d' &&
size.width === size.height &&
size.depthOrArrayLayers === 6 &&
!isCompressedTextureFormat(descriptor.format)
) {
descriptor.textureBindingViewDimension = 'cube';
}
return descriptor;
}

class ImageCopyTest extends AllFeaturesMaxLimitsGPUTest {
/**
* This is used for testing passing undefined members of `GPUTexelCopyBufferLayout` instead of actual
Expand Down Expand Up @@ -648,13 +667,16 @@ class ImageCopyTest extends AllFeaturesMaxLimitsGPUTest {
checkMethod: CheckMethod;
changeBeforePass?: ChangeBeforePass;
}): void {
const texture = this.createTextureTracked({
size: textureSize as [number, number, number],
format,
dimension,
mipLevelCount: mipLevel + 1,
usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING,
});
const texture = this.createTextureTracked(
applyTextureBindingViewDimensionForTest({
size: textureSize as [number, number, number],
format,
dimension,
mipLevelCount: mipLevel + 1,
usage:
GPUTextureUsage.COPY_SRC | GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING,
})
);

const data = dataGenerator.generateView(dataSize);

Expand Down Expand Up @@ -1186,6 +1208,12 @@ const kRowsPerImageAndBytesPerRowParams = {
{ copyWidthInBlocks: 5, copyHeightInBlocks: 4, copyDepth: 1 }, // copyDepth = 1

{ copyWidthInBlocks: 7, copyHeightInBlocks: 1, copyDepth: 1 }, // copyHeight = 1 and copyDepth = 1

// These 2 cases are for textureBindingViewDimension: 'cube'
// Note: this is indirect. The fact that the copyWidthInBlocks and copyHeightInBlocks
// are equal makes it possible to use as a cube
{ copyWidthInBlocks: 3, copyHeightInBlocks: 3, copyDepth: 1 },
{ copyWidthInBlocks: 4, copyHeightInBlocks: 4, copyDepth: 6 },
],

// Copy sizes that are suitable for 1D texture and check both some copy sizes and empty copies.
Expand Down Expand Up @@ -1276,7 +1304,7 @@ bytes in copy works for every format.
textureSize: [
Math.max(copyWidth, info.blockWidth),
Math.max(copyHeight, info.blockHeight),
Math.max(copyDepth, 1),
Math.max(copyDepth, dimension === '2d' ? 6 : 1),
] /* making sure the texture is non-empty */,
format,
dimension,
Expand All @@ -1299,7 +1327,7 @@ const kOffsetsAndSizesParams = {
{ offsetInBlocks: 0, dataPaddingInBytes: 1 }, // dataPaddingInBytes > 0
{ offsetInBlocks: 1, dataPaddingInBytes: 8 }, // offset > 0 and dataPaddingInBytes > 0
],
copyDepth: [1, 2],
copyDepth: [1, 2, 6], // 6 is for textureBindingViewDimension: 'cube'
};

g.test('offsets_and_sizes')
Expand Down Expand Up @@ -1433,8 +1461,8 @@ for all formats. We pass origin and copyExtent as [number, number, number].`
textureFormatAndDimensionPossiblyCompatible(dimension, format)
)
.beginSubcases()
.combine('originValueInBlocks', [0, 7, 8])
.combine('copySizeValueInBlocks', [0, 7, 8])
.combine('originValueInBlocks', [0, 6, 7, 8]) // the 6 is needed for textureBindingViewDimension: 'cube'
.combine('copySizeValueInBlocks', [0, 6, 7, 8]) // the 6 is needed for textureBindingViewDimension: 'cube'
.combine('textureSizePaddingValueInBlocks', [0, 7, 8])
.unless(
p =>
Expand Down Expand Up @@ -1640,6 +1668,13 @@ TODO: Make a variant for depth-stencil formats.
_mipSizeInBlocks: { width: 9, height: 7, depthOrArrayLayers: 4 },
mipLevel: 4,
},
// width === height and depthOrArrayLayers = 6 for textureBindingViewDimension: 'cube'
{
copySizeInBlocks: { width: 5, height: 4, depthOrArrayLayers: 2 },
originInBlocks: { x: 3, y: 2, z: 1 },
_mipSizeInBlocks: { width: 9, height: 9, depthOrArrayLayers: 6 },
mipLevel: 4,
},
])
.expand('textureSize', generateTestTextureSizes)
)
Expand Down
71 changes: 70 additions & 1 deletion src/webgpu/api/operation/reflection.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ Tests that object attributes which reflect the object's creation properties are

import { makeTestGroup } from '../../../common/framework/test_group.js';
import { GPUConst } from '../../constants.js';
import { AllFeaturesMaxLimitsGPUTest } from '../../gpu_test.js';
import { AllFeaturesMaxLimitsGPUTest, GPUTest } from '../../gpu_test.js';
import { reifyExtent3D } from '../../util/unions.js';

export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);

Expand Down Expand Up @@ -90,6 +91,7 @@ const kTextureSubcases: readonly {
mipLevelCount?: number;
label?: string;
dimension?: GPUTextureDimension;
textureBindingViewDimension?: GPUTextureViewDimension;
sampleCount?: number;
invalid?: boolean;
}[] = [
Expand All @@ -115,18 +117,59 @@ const kTextureSubcases: readonly {
usage: GPUConst.TextureUsage.TEXTURE_BINDING,
mipLevelCount: 2,
},
{
size: [4, 4],
format: 'rgba8unorm',
usage: GPUConst.TextureUsage.TEXTURE_BINDING,
mipLevelCount: 2,
textureBindingViewDimension: '2d',
},
{
size: [4, 4],
format: 'rgba8unorm',
usage: GPUConst.TextureUsage.TEXTURE_BINDING,
mipLevelCount: 2,
textureBindingViewDimension: '2d-array',
},
{
size: [4, 4, 4],
format: 'rgba8unorm',
usage: GPUConst.TextureUsage.TEXTURE_BINDING,
mipLevelCount: 2,
},
{
size: [16, 16, 16],
format: 'rgba8unorm',
usage: GPUConst.TextureUsage.TEXTURE_BINDING,
dimension: '3d',
},
{
size: [16, 16, 16],
format: 'rgba8unorm',
usage: GPUConst.TextureUsage.TEXTURE_BINDING,
dimension: '3d',
textureBindingViewDimension: '3d',
},
{
size: [16, 16, 6],
format: 'rgba8unorm',
usage: GPUConst.TextureUsage.TEXTURE_BINDING,
dimension: '2d',
textureBindingViewDimension: 'cube',
},
{
size: [32],
format: 'rgba8unorm',
usage: GPUConst.TextureUsage.TEXTURE_BINDING,
dimension: '1d',
},
{
size: [32],
format: 'rgba8unorm',
usage: GPUConst.TextureUsage.TEXTURE_BINDING,
dimension: '1d',
textureBindingViewDimension: '1d',
},
{
size: { width: 4, height: 4 },
format: 'rgba8unorm',
Expand All @@ -142,6 +185,28 @@ const kTextureSubcases: readonly {
},
] as const;

function getExpectedTextureBindingViewDimension(
t: GPUTest,
descriptor: GPUTextureDescriptor
): GPUTextureViewDimension | undefined {
if (t.isCompatibility) {
if (descriptor.textureBindingViewDimension) {
return descriptor.textureBindingViewDimension;
}
switch (descriptor.dimension) {
case '1d':
return '1d';
case '2d':
case undefined:
return reifyExtent3D(descriptor.size).depthOrArrayLayers > 1 ? '2d-array' : '2d';
case '3d':
return '3d';
}
} else {
return undefined;
}
}

g.test('texture_reflection_attributes')
.desc(`For every texture attribute, the corresponding descriptor value is carried over.`)
.paramsSubcasesOnly(u => u.combine('descriptor', kTextureSubcases))
Expand Down Expand Up @@ -172,6 +237,10 @@ g.test('texture_reflection_attributes')
t.expect(texture.dimension === (descriptor.dimension || '2d'));
t.expect(texture.mipLevelCount === (descriptor.mipLevelCount || 1));
t.expect(texture.sampleCount === (descriptor.sampleCount || 1));
t.expect(
texture.textureBindingViewDimension ===
getExpectedTextureBindingViewDimension(t, descriptor)
);
}, descriptor.invalid === true);
});

Expand Down
16 changes: 16 additions & 0 deletions src/webgpu/format_info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2399,6 +2399,22 @@ export function isTextureFormatBlendable(device: GPUDevice, format: GPUTextureFo
return !!kAllTextureFormatInfo[format].colorRender?.blend;
}

/**
* Returns true if a texture can be filtered.
*/
export function isTextureFormatFilterable(device: GPUDevice, format: GPUTextureFormat): boolean {
const type = getTextureFormatType(format);
switch (type) {
case 'float':
return true;
case 'unfilterable-float':
assert(is32Float(format));
return hasFeature(device.features, 'float32-filterable');
default:
return false;
}
}

/**
* Returns the texture's type (float, unsigned-float, sint, uint, depth)
*/
Expand Down
20 changes: 4 additions & 16 deletions src/webgpu/gpu_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ import {
getRequiredFeatureForTextureFormat,
isTextureFormatUsableAsRenderAttachment,
isTextureFormatMultisampled,
is32Float,
isSintOrUintFormat,
isTextureFormatResolvable,
isDepthTextureFormat,
isStencilTextureFormat,
textureViewDimensionAndFormatCompatibleForDevice,
textureDimensionAndFormatCompatibleForDevice,
isTextureFormatUsableWithStorageAccessMode,
isTextureFormatUsableWithCopyExternalImageToTexture,
isTextureFormatFilterable,
isTextureFormatBlendable,
} from './format_info.js';
import { checkElementsEqual, checkElementsBetween } from './util/check_contents.js';
import { CommandBufferMaker, EncoderType } from './util/command_buffer_maker.js';
Expand Down Expand Up @@ -604,26 +604,14 @@ export class GPUTestBase extends Fixture<GPUTestSubcaseBatchState> {
skipIfTextureFormatNotBlendable(...formats: (GPUTextureFormat | undefined)[]) {
for (const format of formats) {
if (format === undefined) continue;
this.skipIf(isSintOrUintFormat(format), 'sint/uint formats are not blendable');
if (is32Float(format)) {
this.skipIf(
!hasFeature(this.device.features, 'float32-blendable'),
`texture format '${format}' is not blendable`
);
}
this.skipIf(!isTextureFormatBlendable(this.device, format), `${format} is not blendable`);
}
}

skipIfTextureFormatNotFilterable(...formats: (GPUTextureFormat | undefined)[]) {
for (const format of formats) {
if (format === undefined) continue;
this.skipIf(isSintOrUintFormat(format), 'sint/uint formats are not filterable');
if (is32Float(format)) {
this.skipIf(
!hasFeature(this.device.features, 'float32-filterable'),
`texture format '${format}' is not filterable`
);
}
this.skipIf(!isTextureFormatFilterable(this.device, format), `${format} is not filterable`);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,7 @@ export function skipIfTextureFormatNotSupportedOrNeedsFilteringAndIsUnfilterable
) {
t.skipIfTextureFormatNotSupported(format);
if (filter === 'linear') {
t.skipIf(isDepthTextureFormat(format), 'depth texture are unfilterable');

const type = getTextureFormatType(format);
if (type === 'unfilterable-float') {
assert(is32Float(format));
t.skipIfDeviceDoesNotHaveFeature('float32-filterable');
}
t.skipIfTextureFormatNotFilterable(format);
}
}

Expand Down