Skip to content

Commit

Permalink
Fix readTextureToTexelViews to handle all types of formats and viewDi…
Browse files Browse the repository at this point in the history
…mensions

Issue #4017
  • Loading branch information
greggman committed Oct 31, 2024
1 parent bfbfd9e commit 0905779
Show file tree
Hide file tree
Showing 3 changed files with 241 additions and 41 deletions.
11 changes: 10 additions & 1 deletion src/webgpu/format_info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1817,12 +1817,21 @@ export function isRegularTextureFormat(format: GPUTextureFormat) {
}

/**
* Returns true of format is both compressed and a float format, for example 'bc6h-rgb-ufloat'.
* Returns true if format is both compressed and a float format, for example 'bc6h-rgb-ufloat'.
*/
export function isCompressedFloatTextureFormat(format: GPUTextureFormat) {
return isCompressedTextureFormat(format) && format.includes('float');
}

/**
* Returns true if format is uint or uint
*/
export function isSintOrUintFormat(format: GPUTextureFormat) {
const info = kTextureFormatInfo[format];
const type = info.color?.type ?? info.depth?.type ?? info.stencil?.type;
return type === 'sint' || type === 'uint';
}

/**
* Returns true of format can be multisampled.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
export const description = `
Tests for texture_utils.ts
`;

import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { assert } from '../../../../../../common/util/util.js';
import { GPUTest } from '../../../../../gpu_test.js';
import { getTextureDimensionFromView, virtualMipSize } from '../../../../../util/texture/base.js';
import {
kTexelRepresentationInfo,
PerTexelComponent,
TexelRepresentationInfo,
} from '../../../../../util/texture/texel_data.js';

import {
chooseTextureSize,
createTextureWithRandomDataAndGetTexels,
isSupportedViewFormatCombo,
readTextureToTexelViews,
texelsApproximatelyEqual,
} from './texture_utils.js';

export const g = makeTestGroup(GPUTest);

function texelFormat(texel: Readonly<PerTexelComponent<number>>, rep: TexelRepresentationInfo) {
return rep.componentOrder.map(component => `${component}: ${texel[component]}`).join(', ');
}

g.test('readTextureToTexelViews')
.desc('test readTextureToTexelViews for various formats and dimensions')
.params(u =>
u
.combineWithParams([
{ srcFormat: 'r8unorm', texelViewFormat: 'rgba32float' },
{ srcFormat: 'r8sint', texelViewFormat: 'rgba32sint' },
{ srcFormat: 'r8uint', texelViewFormat: 'rgba32uint' },
{ srcFormat: 'rgba32float', texelViewFormat: 'rgba32float' },
{ srcFormat: 'rgba32uint', texelViewFormat: 'rgba32uint' },
{ srcFormat: 'rgba32sint', texelViewFormat: 'rgba32sint' },
{ srcFormat: 'depth24plus', texelViewFormat: 'rgba32float' },
{ srcFormat: 'depth24plus-stencil8', texelViewFormat: 'r32float' },
{ srcFormat: 'stencil8', texelViewFormat: 'rgba32sint' },
] as const)
.combine('viewDimension', ['1d', '2d', '2d-array', '3d', 'cube', 'cube-array'] as const)
.filter(t => isSupportedViewFormatCombo(t.srcFormat, t.viewDimension))
)
.beforeAllSubcases(t => {
t.skipIfTextureViewDimensionNotSupported(t.params.viewDimension);
})
.fn(async t => {
const { srcFormat, texelViewFormat, viewDimension } = 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,
usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING,
...(t.isCompatibility && { textureBindingViewDimension: viewDimension }),
};
const { texels: expectedTexelViews, texture } = await createTextureWithRandomDataAndGetTexels(
t,
descriptor
);
const actualTexelViews = await readTextureToTexelViews(t, texture, descriptor, texelViewFormat);

assert(actualTexelViews.length === expectedTexelViews.length, 'num mip levels match');

const errors = [];
for (let mipLevel = 0; mipLevel < actualTexelViews.length; ++mipLevel) {
const actualMipLevelTexelView = actualTexelViews[mipLevel];
const expectedMipLevelTexelView = expectedTexelViews[mipLevel];
const mipLevelSize = virtualMipSize(texture.dimension, size, mipLevel);

const actualRep = kTexelRepresentationInfo[actualMipLevelTexelView.format];
const expectedRep = kTexelRepresentationInfo[expectedMipLevelTexelView.format];

for (let z = 0; z < mipLevelSize[2]; ++z) {
for (let y = 0; y < mipLevelSize[1]; ++y) {
for (let x = 0; x < mipLevelSize[0]; ++x) {
const actual = actualMipLevelTexelView.color({ x, y, z });
const expected = expectedMipLevelTexelView.color({ x, y, z });
// This currently expects the exact same values in actual vs expected.
// It's possible this needs to be relaxed slightly but only for non-integer formats.
// For now, if the tests pass everywhere, we'll keep it at 0 tolerance.
const maxFractionalDiff = 0;
if (
!texelsApproximatelyEqual(
actual,
actualMipLevelTexelView.format,
expected,
expectedMipLevelTexelView.format,
maxFractionalDiff
)
) {
const actualStr = texelFormat(actual, actualRep);
const expectedStr = texelFormat(expected, expectedRep);
errors.push(
`texel at ${x}, ${y}, ${z}, expected: ${expectedStr}, actual: ${actualStr}`
);
}
}
}
}

assert(errors.length === 0, errors.join('\n'));
}
});
Loading

0 comments on commit 0905779

Please sign in to comment.