Commit b3a83588 authored by Kai Ninomiya's avatar Kai Ninomiya Committed by Chromium LUCI CQ

Roll WebGPU CTS

Bug: None
Change-Id: Id90263faf4aade3b39742cb0db6c5c9109429e2f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2570206
Auto-Submit: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: default avatarAustin Eng <enga@chromium.org>
Cr-Commit-Position: refs/heads/master@{#833413}
parent f0976807
......@@ -1541,7 +1541,7 @@ deps = {
Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '3e3f152617996e4a175fd0b597a1936824b371f3',
'src/third_party/webgpu-cts/src':
Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'a8196844ac6228fea3560b82d29e01fed2fbf13a',
Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '6d2646eeb60e313cacbe43b266cf32948a4fef7e',
'src/third_party/blink/web_tests/wpt_internal/webgpu/third_party/glslang_js': {
'packages': [
......
// AUTO-GENERATED - DO NOT EDIT. See tools/gen_version.
export const version = 'a8196844ac6228fea3560b82d29e01fed2fbf13a';
export const version = '6d2646eeb60e313cacbe43b266cf32948a4fef7e';
......@@ -243,4 +243,12 @@ export class BufferSyncTest extends GPUTest {
bufferData[0] = expectedValue;
this.expectContents(buffer, bufferData);
}
verifyDataTwoValidValues(buffer, expectedValue1, expectedValue2) {
const bufferData1 = new Uint32Array(1);
bufferData1[0] = expectedValue1;
const bufferData2 = new Uint32Array(1);
bufferData2[0] = expectedValue2;
this.expectContentsTwoValidValues(buffer, bufferData1, bufferData2);
}
}
......@@ -12,7 +12,7 @@ Wait on another fence, then call expectContents to verify the written buffer.
- if pass type is the same, x= {single pass, separate passes} (note: render has loose guarantees)
- if not single pass, x= writes in {same cmdbuf, separate cmdbufs, separate submits, separate queues}
`;
import { poptions, params } from '../../../../../common/framework/params_builder.js';
import { pbool, poptions, params } from '../../../../../common/framework/params_builder.js';
import { makeTestGroup } from '../../../../../common/framework/test_group.js';
import { kAllWriteOps, BufferSyncTest } from './buffer_sync_test.js';
......@@ -84,7 +84,60 @@ g.test('two_draws_in_the_same_render_pass')
a storage buffer. The second write will write 2 into the same buffer in the same pass. Expected
data in buffer is either 1 or 2. It may use bundle in each draw.`
)
.unimplemented();
.params(params().combine(pbool('firstDrawUseBundle')).combine(pbool('secondDrawUseBundle')))
.fn(async t => {
const { firstDrawUseBundle, secondDrawUseBundle } = t.params;
const buffer = await t.createBufferWithValue(0);
const encoder = t.device.createCommandEncoder();
const passEncoder = t.beginSimpleRenderPass(encoder);
const useBundle = [firstDrawUseBundle, secondDrawUseBundle];
for (let i = 0; i < 2; ++i) {
const renderEncoder = useBundle[i]
? t.device.createRenderBundleEncoder({
colorFormats: ['rgba8unorm'],
})
: passEncoder;
const pipeline = t.createStorageWriteRenderPipeline(i + 1);
const bindGroup = t.createBindGroup(pipeline, buffer);
renderEncoder.setPipeline(pipeline);
renderEncoder.setBindGroup(0, bindGroup);
renderEncoder.draw(1, 1, 0, 0);
if (useBundle[i]) passEncoder.executeBundles([renderEncoder.finish()]);
}
passEncoder.endPass();
t.device.defaultQueue.submit([encoder.finish()]);
t.verifyDataTwoValidValues(buffer, 1, 2);
});
g.test('two_draws_in_the_same_render_bundle')
.desc(
`Test write-after-write operations in the same render bundle. The first write will write 1 into
a storage buffer. The second write will write 2 into the same buffer in the same pass. Expected
data in buffer is either 1 or 2.`
)
.fn(async t => {
const buffer = await t.createBufferWithValue(0);
const encoder = t.device.createCommandEncoder();
const passEncoder = t.beginSimpleRenderPass(encoder);
const renderEncoder = t.device.createRenderBundleEncoder({
colorFormats: ['rgba8unorm'],
});
for (let i = 0; i < 2; ++i) {
const pipeline = t.createStorageWriteRenderPipeline(i + 1);
const bindGroup = t.createBindGroup(pipeline, buffer);
renderEncoder.setPipeline(pipeline);
renderEncoder.setBindGroup(0, bindGroup);
renderEncoder.draw(1, 1, 0, 0);
}
passEncoder.executeBundles([renderEncoder.finish()]);
passEncoder.endPass();
t.device.defaultQueue.submit([encoder.finish()]);
t.verifyDataTwoValidValues(buffer, 1, 2);
});
g.test('two_dispatches_in_the_same_compute_pass')
.desc(
......@@ -92,4 +145,20 @@ g.test('two_dispatches_in_the_same_compute_pass')
a storage buffer. The second write will write 2 into the same buffer in the same pass. Expected
data in buffer is 2.`
)
.unimplemented();
.fn(async t => {
const buffer = await t.createBufferWithValue(0);
const encoder = t.device.createCommandEncoder();
const pass = encoder.beginComputePass();
for (let i = 0; i < 2; ++i) {
const pipeline = t.createStorageWriteComputePipeline(i + 1);
const bindGroup = t.createBindGroup(pipeline, buffer);
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatch(1);
}
pass.endPass();
t.device.defaultQueue.submit([encoder.finish()]);
t.verifyData(buffer, 2);
});
......@@ -21,53 +21,50 @@ class F extends ValidationTest {
const format = colorStates.length ? colorStates[0].format : 'rgba8unorm';
return {
vertexStage: this.getVertexStage(),
fragmentStage: this.getFragmentStage(format),
layout: this.getPipelineLayout(),
primitiveTopology,
colorStates,
sampleCount,
depthStencilState,
};
let fragColorType;
let suffix;
if (format.endsWith('sint')) {
fragColorType = 'i32';
suffix = '';
} else if (format.endsWith('uint')) {
fragColorType = 'u32';
suffix = 'u';
} else {
fragColorType = 'f32';
suffix = '.0';
}
getVertexStage() {
return {
module: this.makeShaderModule('vertex', {
glsl: `
#version 450
void main() {
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
}
`,
vertexStage: {
module: this.device.createShaderModule({
code: `
[[builtin(position)]] var<out> Position : vec4<f32>;
[[stage(vertex)]] fn main() -> void {
Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
}`,
}),
entryPoint: 'main',
};
}
getFragmentStage(format) {
let fragColorType;
if (format.endsWith('sint')) {
fragColorType = 'ivec4';
} else if (format.endsWith('uint')) {
fragColorType = 'uvec4';
} else {
fragColorType = 'vec4';
}
},
const glsl = `
#version 450
layout(location = 0) out ${fragColorType} fragColor;
void main() {
fragColor = ${fragColorType}(0.0, 1.0, 0.0, 1.0);
}
`;
fragmentStage: {
module: this.device.createShaderModule({
code: `
[[location(0)]] var<out> fragColor : vec4<${fragColorType}>;
[[stage(fragment)]] fn main() -> void {
fragColor = vec4<${fragColorType}>(0${suffix}, 1${suffix}, 0${suffix}, 1${suffix});
}`,
}),
return {
module: this.makeShaderModule('fragment', { glsl }),
entryPoint: 'main',
},
layout: this.getPipelineLayout(),
primitiveTopology,
colorStates,
sampleCount,
depthStencilState,
};
}
......
......@@ -155,6 +155,32 @@ export class GPUTest extends Fixture {
});
}
// We can expand this function in order to support multiple valid values or two mixed vectors
// if needed. See the discussion at https://github.com/gpuweb/cts/pull/384#discussion_r533101429
expectContentsTwoValidValues(src, expected1, expected2, srcOffset = 0) {
assert(expected1.byteLength === expected2.byteLength);
const { dst, begin, end } = this.createAlignedCopyForMapRead(
src,
expected1.byteLength,
srcOffset
);
this.eventualAsyncExpectation(async niceStack => {
const constructor = expected1.constructor;
await dst.mapAsync(GPUMapMode.READ);
const actual = new constructor(dst.getMappedRange());
const check1 = this.checkBuffer(actual.subarray(begin, end), expected1);
const check2 = this.checkBuffer(actual.subarray(begin, end), expected2);
if (check1 !== undefined && check2 !== undefined) {
niceStack.message = `Expected one of the following two checks to succeed:
- ${check1}
- ${check2}`;
this.rec.expectationFailed(niceStack);
}
dst.destroy();
});
}
expectBuffer(actual, exp) {
const check = this.checkBuffer(actual, exp);
if (check !== undefined) {
......
......@@ -145,18 +145,14 @@ class DrawCall {
size -= 1;
}
const vertexBuffer = this.device.createBuffer({
mappedAtCreation: true,
size,
usage: GPUBufferUsage.VERTEX,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
});
const vertexMapping = vertexBuffer.getMappedRange();
if (!partialLastNumber) {
new Float32Array(vertexMapping).set(vertexArray);
} else {
new Uint8Array(vertexMapping).set(new Uint8Array(vertexArray.buffer).slice(0, size));
if (partialLastNumber) {
size -= 3;
}
vertexBuffer.unmap();
this.device.defaultQueue.writeBuffer(vertexBuffer, 0, vertexArray, size);
return vertexBuffer;
}
......@@ -164,11 +160,10 @@ class DrawCall {
generateIndexBuffer(indexArray) {
const indexBuffer = this.device.createBuffer({
size: indexArray.byteLength,
usage: GPUBufferUsage.INDEX,
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
});
new Uint16Array(indexBuffer.getMappedRange()).set(indexArray);
indexBuffer.unmap();
this.device.defaultQueue.writeBuffer(indexBuffer, 0, indexArray);
return indexBuffer;
}
......@@ -218,28 +213,28 @@ class DrawCall {
const typeInfoMap = {
float: {
format: 'float',
wgslType: 'f32',
size: 4,
validationFunc: 'return valid(v);',
},
vec2: {
format: 'float2',
float2: {
wgslType: 'vec2<f32>',
size: 8,
validationFunc: 'return valid(v.x) && valid(v.y);',
},
vec3: {
format: 'float3',
float3: {
wgslType: 'vec3<f32>',
size: 12,
validationFunc: 'return valid(v.x) && valid(v.y) && valid(v.z);',
},
vec4: {
format: 'float4',
float4: {
wgslType: 'vec4<f32>',
size: 16,
validationFunc: `return valid(v.x) && valid(v.y) && valid(v.z) && valid(v.w) ||
v.x == 0 && v.y == 0 && v.z == 0 && (v.w == 0.0 || v.w == 1.0);`,
v.x == 0.0 && v.y == 0.0 && v.z == 0.0 && (v.w == 0.0 || v.w == 1.0);`,
},
};
......@@ -266,8 +261,8 @@ g.test('vertexAccess')
const p = t.params;
const typeInfo = typeInfoMap[p.type];
// Number of vertices to draw
const numVertices = 3;
// Number of vertices to draw, odd so that uint16 index buffers are aligned to size 4
const numVertices = 4;
// Each buffer will be bound to this many attributes (2 would mean 2 attributes per buffer)
const attributesPerBuffer = 2;
// Make an array big enough for the vertices, attributes, and size of each element
......@@ -304,7 +299,7 @@ g.test('vertexAccess')
let currAttribute = 0;
for (let i = 0; i < bufferContents.length; i++) {
for (let j = 0; j < attributesPerBuffer; j++) {
layoutStr += `layout(location=${currAttribute}) in ${p.type} a_${currAttribute};\n`;
layoutStr += `[[location(${currAttribute})]] var<in> a_${currAttribute} : ${typeInfo.wgslType};\n`;
attributeNames.push(`a_${currAttribute}`);
currAttribute++;
}
......@@ -324,7 +319,7 @@ g.test('vertexAccess')
.map((_, i) => ({
shaderLocation: currAttribute++,
offset: i * typeInfo.size,
format: typeInfo.format,
format: p.type,
})),
});
}
......@@ -336,47 +331,60 @@ g.test('vertexAccess')
vertexIndexOffset += p.errorScale;
}
// Construct pipeline that outputs a red fragment, only if we notice any invalid values
const vertexModule = t.makeShaderModule('vertex', {
glsl: `
#version 450
const pipeline = t.device.createRenderPipeline({
vertexStage: {
module: t.device.createShaderModule({
code: `
[[builtin(position)]] var<out> Position : vec4<f32>;
[[builtin(vertex_idx)]] var<in> VertexIndex : u32;
${layoutStr}
bool valid(float f) {
return ${validValues.map(v => `f == ${v}`).join(' || ')};
fn valid(f : f32) -> bool {
return ${validValues.map(v => `f == ${v}.0`).join(' || ')};
}
bool validationFunc(${p.type} v) {
fn validationFunc(v : ${typeInfo.wgslType}) -> bool {
${typeInfo.validationFunc}
}
void main() {
bool attributesInBounds = ${attributeNames.map(a => `validationFunc(${a})`).join(' && ')};
bool indexInBounds = gl_VertexIndex == 0 || (gl_VertexIndex >= ${vertexIndexOffset} &&
gl_VertexIndex < ${vertexIndexOffset + numVertices});
[[stage(vertex)]] fn main() -> void {
var attributesInBounds : bool = ${attributeNames
.map(a => `validationFunc(${a})`)
.join(' && ')};
var indexInBounds : bool = VertexIndex == 0u ||
(VertexIndex >= ${vertexIndexOffset}u &&
VertexIndex < ${vertexIndexOffset + numVertices}u);
if (attributesInBounds && (${!p.indexed} || indexInBounds)) {
// Success case, move the vertex out of the viewport
gl_Position = vec4(-1.0, 0.0, 0.0, 1.0);
# Success case, move the vertex out of the viewport
Position = vec4<f32>(-1.0, 0.0, 0.0, 1.0);
} else {
// Failure case, move the vertex inside the viewport
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
}
# Failure case, move the vertex inside the viewport
Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
}
`,
});
}`,
}),
const fragmentModule = t.makeShaderModule('fragment', {
glsl: `
#version 450
precision mediump float;
entryPoint: 'main',
},
layout(location = 0) out vec4 fragColor;
fragmentStage: {
module: t.device.createShaderModule({
code: `
[[location(0)]] var<out> fragColor : vec4<f32>;
[[stage(fragment)]] fn main() -> void {
fragColor = vec4<f32>(1.0, 0.0, 0.0, 1.0);
}`,
}),
void main() {
fragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`,
entryPoint: 'main',
},
primitiveTopology: 'point-list',
colorStates: [{ format: 'rgba8unorm' }],
vertexState: {
vertexBuffers,
},
});
// Pipeline setup, texture setup
......@@ -388,17 +396,6 @@ g.test('vertexAccess')
const colorAttachmentView = colorAttachment.createView();
const pipeline = t.device.createRenderPipeline({
vertexStage: { module: vertexModule, entryPoint: 'main' },
fragmentStage: { module: fragmentModule, entryPoint: 'main' },
primitiveTopology: 'point-list',
colorStates: [{ format: 'rgba8unorm', alphaBlend: {}, colorBlend: {} }],
vertexState: {
indexFormat: 'uint16',
vertexBuffers,
},
});
// Offset the draw call parameter we are testing by |errorScale|
draw[p.drawCallTestParameter] += p.errorScale;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment