summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/mozilla/tests/webgpu/webgpu/gpu_test.js
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/mozilla/tests/webgpu/webgpu/gpu_test.js')
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/webgpu/gpu_test.js234
1 files changed, 200 insertions, 34 deletions
diff --git a/testing/web-platform/mozilla/tests/webgpu/webgpu/gpu_test.js b/testing/web-platform/mozilla/tests/webgpu/webgpu/gpu_test.js
index 2b589b9316..0354abdbe7 100644
--- a/testing/web-platform/mozilla/tests/webgpu/webgpu/gpu_test.js
+++ b/testing/web-platform/mozilla/tests/webgpu/webgpu/gpu_test.js
@@ -9,6 +9,7 @@
'../common/framework/fixture.js';
import { globalTestConfig } from '../common/framework/test_config.js';
+import { getGPU } from '../common/util/navigator_gpu.js';
import {
assert,
makeValueTestVariant,
@@ -20,15 +21,22 @@ import {
unreachable } from
'../common/util/util.js';
-import { getDefaultLimits, kQueryTypeInfo } from './capability_info.js';
+import {
+ getDefaultLimits,
+
+ kQueryTypeInfo } from
+
+'./capability_info.js';
+
import {
kTextureFormatInfo,
kEncodableTextureFormats,
resolvePerAspectFormat,
- isCompressedTextureFormat } from
+ isCompressedTextureFormat,
+ isTextureFormatUsableAsStorageFormat } from
'./format_info.js';
import { makeBufferWithContents } from './util/buffer.js';
import { checkElementsEqual, checkElementsBetween } from './util/check_contents.js';
@@ -52,7 +60,7 @@ import {
textureContentIsOKByT2B } from
'./util/texture/texture_ok.js';
import { createTextureFromTexelView, createTextureFromTexelViews } from './util/texture.js';
-import { reifyOrigin3D } from './util/unions.js';
+import { reifyExtent3D, reifyOrigin3D } from './util/unions.js';
const devicePool = new DevicePool();
@@ -245,6 +253,56 @@ export class GPUTestSubcaseBatchState extends SubcaseBatchState {
}
}
}
+
+ skipIfTextureFormatNotUsableAsStorageTexture(...formats) {
+ for (const format of formats) {
+ if (format && !isTextureFormatUsableAsStorageFormat(format, this.isCompatibility)) {
+ this.skip(`Texture with ${format} is not usable as a storage texture`);
+ }
+ }
+ }
+
+ /**
+ * Skips test if the given interpolation type or sampling is not supported.
+ */
+ skipIfInterpolationTypeOrSamplingNotSupported({
+ type,
+ sampling
+
+
+
+ }) {
+ if (this.isCompatibility) {
+ this.skipIf(
+ type === 'linear',
+ 'interpolation type linear is not supported in compatibility mode'
+ );
+ this.skipIf(
+ sampling === 'sample',
+ 'interpolation type linear is not supported in compatibility mode'
+ );
+ }
+ }
+
+ /** Skips this test case if the `langFeature` is *not* supported. */
+ skipIfLanguageFeatureNotSupported(langFeature) {
+ if (!this.hasLanguageFeature(langFeature)) {
+ this.skip(`WGSL language feature '${langFeature}' is not supported`);
+ }
+ }
+
+ /** Skips this test case if the `langFeature` is supported. */
+ skipIfLanguageFeatureSupported(langFeature) {
+ if (this.hasLanguageFeature(langFeature)) {
+ this.skip(`WGSL language feature '${langFeature}' is supported`);
+ }
+ }
+
+ /** returns true iff the `langFeature` is supported */
+ hasLanguageFeature(langFeature) {
+ const lf = getGPU(this.recorder).wgslLanguageFeatures;
+ return lf !== undefined && lf.has(langFeature);
+ }
}
/**
@@ -420,6 +478,26 @@ export class GPUTestBase extends Fixture {
}
}
+ /** Skips this test case if the `langFeature` is *not* supported. */
+ skipIfLanguageFeatureNotSupported(langFeature) {
+ if (!this.hasLanguageFeature(langFeature)) {
+ this.skip(`WGSL language feature '${langFeature}' is not supported`);
+ }
+ }
+
+ /** Skips this test case if the `langFeature` is supported. */
+ skipIfLanguageFeatureSupported(langFeature) {
+ if (this.hasLanguageFeature(langFeature)) {
+ this.skip(`WGSL language feature '${langFeature}' is supported`);
+ }
+ }
+
+ /** returns true iff the `langFeature` is supported */
+ hasLanguageFeature(langFeature) {
+ const lf = getGPU(this.rec).wgslLanguageFeatures;
+ return lf !== undefined && lf.has(langFeature);
+ }
+
/**
* Expect a GPUBuffer's contents to pass the provided check.
*
@@ -730,7 +808,8 @@ export class GPUTestBase extends Fixture {
slice = 0,
layout,
generateWarningOnly = false,
- checkElementsBetweenFn = (act, [a, b]) => checkElementsBetween(act, [(i) => a[i], (i) => b[i]])
+ checkElementsBetweenFn = (act, [a, b]) =>
+ checkElementsBetween(act, [(i) => a[i], (i) => b[i]])
@@ -757,24 +836,32 @@ export class GPUTestBase extends Fixture {
/**
* Emulate a texture to buffer copy by using a compute shader
- * to load texture value of a single pixel and write to a storage buffer.
- * For sample count == 1, the buffer contains only one value of the sample.
- * For sample count > 1, the buffer contains (N = sampleCount) values sorted
+ * to load texture values of a subregion of a 2d texture and write to a storage buffer.
+ * For sample count == 1, the buffer contains extent[0] * extent[1] of the sample.
+ * For sample count > 1, the buffer contains extent[0] * extent[1] * (N = sampleCount) values sorted
* in the order of their sample index [0, sampleCount - 1]
*
* This can be useful when the texture to buffer copy is not available to the texture format
* e.g. (depth24plus), or when the texture is multisampled.
*
- * MAINTENANCE_TODO: extend to read multiple pixels with given origin and size.
+ * MAINTENANCE_TODO: extend texture dimension to 1d and 3d.
*
* @returns storage buffer containing the copied value from the texture.
*/
- copySinglePixelTextureToBufferUsingComputePass(
+ copy2DTextureToBufferUsingComputePass(
type,
componentCount,
textureView,
- sampleCount)
+ sampleCount = 1,
+ extent_ = [1, 1, 1],
+ origin_ = [0, 0, 0])
{
+ const origin = reifyOrigin3D(origin_);
+ const extent = reifyExtent3D(extent_);
+ const width = extent.width;
+ const height = extent.height;
+ const kWorkgroupSizeX = 8;
+ const kWorkgroupSizeY = 8;
const textureSrcCode =
sampleCount === 1 ?
`@group(0) @binding(0) var src: texture_2d<${type}>;` :
@@ -787,13 +874,24 @@ export class GPUTestBase extends Fixture {
${textureSrcCode}
@group(0) @binding(1) var<storage, read_write> dst : Buffer;
- @compute @workgroup_size(1) fn main() {
- var coord = vec2<i32>(0, 0);
- for (var sampleIndex = 0; sampleIndex < ${sampleCount};
+ struct Params {
+ origin: vec2u,
+ extent: vec2u,
+ };
+ @group(0) @binding(2) var<uniform> params : Params;
+
+ @compute @workgroup_size(${kWorkgroupSizeX}, ${kWorkgroupSizeY}, 1) fn main(@builtin(global_invocation_id) id : vec3u) {
+ let boundary = params.origin + params.extent;
+ let coord = params.origin + id.xy;
+ if (any(coord >= boundary)) {
+ return;
+ }
+ let offset = (id.x + id.y * params.extent.x) * ${componentCount} * ${sampleCount};
+ for (var sampleIndex = 0u; sampleIndex < ${sampleCount};
sampleIndex = sampleIndex + 1) {
- let o = sampleIndex * ${componentCount};
- let v = textureLoad(src, coord, sampleIndex);
- for (var component = 0; component < ${componentCount}; component = component + 1) {
+ let o = offset + sampleIndex * ${componentCount};
+ let v = textureLoad(src, coord.xy, sampleIndex);
+ for (var component = 0u; component < ${componentCount}; component = component + 1) {
dst.data[o + component] = v[component];
}
}
@@ -810,11 +908,16 @@ export class GPUTestBase extends Fixture {
});
const storageBuffer = this.device.createBuffer({
- size: sampleCount * type.size * componentCount,
+ size: sampleCount * type.size * componentCount * width * height,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC
});
this.trackForCleanup(storageBuffer);
+ const uniformBuffer = this.makeBufferWithContents(
+ new Uint32Array([origin.x, origin.y, width, height]),
+ GPUBufferUsage.UNIFORM
+ );
+
const uniformBindGroup = this.device.createBindGroup({
layout: computePipeline.getBindGroupLayout(0),
entries: [
@@ -827,6 +930,12 @@ export class GPUTestBase extends Fixture {
resource: {
buffer: storageBuffer
}
+ },
+ {
+ binding: 2,
+ resource: {
+ buffer: uniformBuffer
+ }
}]
});
@@ -835,7 +944,11 @@ export class GPUTestBase extends Fixture {
const pass = encoder.beginComputePass();
pass.setPipeline(computePipeline);
pass.setBindGroup(0, uniformBindGroup);
- pass.dispatchWorkgroups(1);
+ pass.dispatchWorkgroups(
+ Math.floor((width + kWorkgroupSizeX - 1) / kWorkgroupSizeX),
+ Math.floor((height + kWorkgroupSizeY - 1) / kWorkgroupSizeY),
+ 1
+ );
pass.end();
this.device.queue.submit([encoder.finish()]);
@@ -1081,11 +1194,17 @@ export class GPUTest extends GPUTestBase {
this.mismatchedProvider = await this.sharedState.acquireMismatchedProvider();
}
+ /** GPUAdapter that the device was created from. */
+ get adapter() {
+ assert(this.provider !== undefined, 'internal error: DeviceProvider missing');
+ return this.provider.adapter;
+ }
+
/**
* GPUDevice for the test to use.
*/
get device() {
- assert(this.provider !== undefined, 'internal error: GPUDevice missing?');
+ assert(this.provider !== undefined, 'internal error: DeviceProvider missing');
return this.provider.device;
}
@@ -1241,13 +1360,30 @@ export class GPUTest extends GPUTestBase {
+
+
const s_deviceToResourcesMap = new WeakMap();
/**
* Gets a (cached) pipeline to render a texture to an rgba8unorm texture
*/
-function getPipelineToRenderTextureToRGB8UnormTexture(device) {
+function getPipelineToRenderTextureToRGB8UnormTexture(
+device,
+texture,
+isCompatibility)
+{
if (!s_deviceToResourcesMap.has(device)) {
+ s_deviceToResourcesMap.set(device, {
+ pipelineByPipelineType: new Map()
+ });
+ }
+
+ const { pipelineByPipelineType } = s_deviceToResourcesMap.get(device);
+ const pipelineType =
+ isCompatibility && texture.depthOrArrayLayers > 1 ? '2d-array' : '2d';
+ if (!pipelineByPipelineType.get(pipelineType)) {
+ const [textureType, layerCode] =
+ pipelineType === '2d' ? ['texture_2d', ''] : ['texture_2d_array', ', uni.baseArrayLayer'];
const module = device.createShaderModule({
code: `
struct VSOutput {
@@ -1255,6 +1391,10 @@ function getPipelineToRenderTextureToRGB8UnormTexture(device) {
@location(0) texcoord: vec2f,
};
+ struct Uniforms {
+ baseArrayLayer: u32,
+ };
+
@vertex fn vs(
@builtin(vertex_index) vertexIndex : u32
) -> VSOutput {
@@ -1275,10 +1415,11 @@ function getPipelineToRenderTextureToRGB8UnormTexture(device) {
}
@group(0) @binding(0) var ourSampler: sampler;
- @group(0) @binding(1) var ourTexture: texture_2d<f32>;
+ @group(0) @binding(1) var ourTexture: ${textureType}<f32>;
+ @group(0) @binding(2) var<uniform> uni: Uniforms;
@fragment fn fs(fsInput: VSOutput) -> @location(0) vec4f {
- return textureSample(ourTexture, ourSampler, fsInput.texcoord);
+ return textureSample(ourTexture, ourSampler, fsInput.texcoord${layerCode});
}
`
});
@@ -1294,10 +1435,10 @@ function getPipelineToRenderTextureToRGB8UnormTexture(device) {
targets: [{ format: 'rgba8unorm' }]
}
});
- s_deviceToResourcesMap.set(device, { pipeline });
+ pipelineByPipelineType.set(pipelineType, pipeline);
}
- const { pipeline } = s_deviceToResourcesMap.get(device);
- return pipeline;
+ const pipeline = pipelineByPipelineType.get(pipelineType);
+ return { pipelineType, pipeline };
}
@@ -1441,7 +1582,11 @@ Base)
// Render every layer of both textures at mipLevel to an rgba8unorm texture
// that matches the size of the mipLevel. After each render, copy the
// result to a buffer and expect the results from both textures to match.
- const pipeline = getPipelineToRenderTextureToRGB8UnormTexture(this.device);
+ const { pipelineType, pipeline } = getPipelineToRenderTextureToRGB8UnormTexture(
+ this.device,
+ actualTexture,
+ this.isCompatibility
+ );
const readbackPromisesPerTexturePerLayer = [actualTexture, expectedTexture].map(
(texture, ndx) => {
const attachmentSize = virtualMipSize('2d', [texture.width, texture.height, 1], mipLevel);
@@ -1457,24 +1602,45 @@ Base)
const numLayers = texture.depthOrArrayLayers;
const readbackPromisesPerLayer = [];
+
+ const uniformBuffer = this.device.createBuffer({
+ size: 4,
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
+ });
+ this.trackForCleanup(uniformBuffer);
+
for (let layer = 0; layer < numLayers; ++layer) {
+ const viewDescriptor = {
+ baseMipLevel: mipLevel,
+ mipLevelCount: 1,
+ ...(!this.isCompatibility && {
+ baseArrayLayer: layer,
+ arrayLayerCount: 1
+ }),
+ dimension: pipelineType
+ };
+
const bindGroup = this.device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{ binding: 0, resource: sampler },
{
binding: 1,
- resource: texture.createView({
- baseMipLevel: mipLevel,
- mipLevelCount: 1,
- baseArrayLayer: layer,
- arrayLayerCount: 1,
- dimension: '2d'
- })
- }]
+ resource: texture.createView(viewDescriptor)
+ },
+ ...(pipelineType === '2d-array' ?
+ [
+ {
+ binding: 2,
+ resource: { buffer: uniformBuffer }
+ }] :
+
+ [])]
});
+ this.device.queue.writeBuffer(uniformBuffer, 0, new Uint32Array([layer]));
+
const encoder = this.device.createCommandEncoder();
const pass = encoder.beginRenderPass({
colorAttachments: [