blob: eba2a31a91a9acd891e2f510c1a5ab631ceeebf5 [file] [log] [blame]
/**
* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
**/ export const description = `Test primitive topology rendering.
Draw a primitive using 6 vertices with each topology and check if the pixel is covered.
Vertex sequence and coordinates are the same for each topology:
- Vertex buffer = [v1, v2, v3, v4, v5, v6]
- Topology = [point-list, line-list, line-strip, triangle-list, triangle-strip]
Test locations are framebuffer coordinates:
- Pixel value { valid: green, invalid: black, format: 'rgba8unorm'}
- Test point is valid if the pixel value equals the covered pixel value at the test location.
- Primitive restart occurs for strips (line-strip and triangle-strip) between [v3, v4].
Topology: point-list Valid test location(s) Invalid test location(s)
v2 v4 v6 Every vertex. Line-strip locations.
Triangle-list locations.
Triangle-strip locations.
v1 v3 v5
Topology: line-list (3 lines)
v2 v4 v6 Center of three line segments: Line-strip locations.
* * * {v1,V2}, {v3,v4}, and {v4,v5}. Triangle-list locations.
* * * Triangle-strip locations.
* * *
v1 v3 v5
Topology: line-strip (5 lines)
v2 v4 v6
** ** *
* * * * * Line-list locations Triangle-list locations.
* ** ** + Center of two line segments: Triangle-strip locations.
v1 v3 v5 {v2,v3} and {v4,v5}.
With primitive restart:
Line segment {v3, v4}.
Topology: triangle-list (2 triangles)
v2 v4 v6
** ****** Center of two triangle(s): Triangle-strip locations.
**** **** {v1,v2,v3} and {v4,v5,v6}.
****** **
v1 v3 v5
Topology: triangle-strip (4 triangles)
v2 v4 v6
** ****** ** ****** Triangle-list locations None.
**** **** **** **** + Center of two triangle(s):
****** ** ****** ** {v2,v3,v4} and {v3,v4,v5}. With primitive restart:
v1 v3 v5 Triangle {v2, v3, v4}
and {v3, v4, v5}.
`;
import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { GPUTest } from '../../../gpu_test.js';
const kRTSize = 56;
const kColorFormat = 'rgba8unorm';
const kValidPixelColor = new Uint8Array([0x00, 0xff, 0x00, 0xff]); // green
const kInvalidPixelColor = new Uint8Array([0x00, 0x00, 0x00, 0x00]); // black
class Point2D {
constructor(x, y) {
this.x = x;
this.y = y;
this.z = 0;
this.w = 1;
}
toNDC() {
// NDC coordinate space is y-up, so we negate the y mapping.
// To ensure the resulting vertex in NDC will be placed at the center of the pixel, we
// must offset by the pixel coordinates or 0.5.
return new Point2D((2 * (this.x + 0.5)) / kRTSize - 1, (-2 * (this.y + 0.5)) / kRTSize + 1);
}
static getMidpoint(a, b) {
return new Point2D((a.x + b.x) / 2, (a.y + b.y) / 2);
}
static getCentroid(a, b, c) {
return new Point2D((a.x + b.x + c.x) / 3, (a.y + b.y + c.y) / 3);
}
}
const VertexLocations = [
new Point2D(8, 24), // v1
new Point2D(16, 8), // v2
new Point2D(24, 24), // v3
new Point2D(32, 8), // v4
new Point2D(40, 24), // v5
new Point2D(48, 8), // v6
];
function getPointTestLocations(expectedColor) {
// Test points are always equal to vertex locations.
const testLocations = [];
for (const location of VertexLocations) {
testLocations.push({ location, color: expectedColor });
}
return testLocations;
}
function getLineTestLocations(expectedColor) {
// Midpoints of 3 line segments
return [
{
// Line {v1, v2}
location: Point2D.getMidpoint(VertexLocations[0], VertexLocations[1]),
color: expectedColor,
},
{
// Line {v3, v4}
location: Point2D.getMidpoint(VertexLocations[2], VertexLocations[3]),
color: expectedColor,
},
{
// Line {v5, v6}
location: Point2D.getMidpoint(VertexLocations[4], VertexLocations[5]),
color: expectedColor,
},
];
}
function getPrimitiveRestartLineTestLocations(expectedColor) {
// Midpoints of 2 line segments
return [
{
// Line {v1, v2}
location: Point2D.getMidpoint(VertexLocations[0], VertexLocations[1]),
color: expectedColor,
},
{
// Line {v5, v6}
location: Point2D.getMidpoint(VertexLocations[4], VertexLocations[5]),
color: expectedColor,
},
];
}
function getLineStripTestLocations(expectedColor) {
// Midpoints of 2 line segments
return [
{
// Line {v2, v3}
location: Point2D.getMidpoint(VertexLocations[1], VertexLocations[2]),
color: expectedColor,
},
{
// Line {v4, v5}
location: Point2D.getMidpoint(VertexLocations[3], VertexLocations[4]),
color: expectedColor,
},
];
}
function getTriangleListTestLocations(expectedColor) {
// Center of two triangles
return [
{
// Triangle {v1, v2, v3}
location: Point2D.getCentroid(VertexLocations[0], VertexLocations[1], VertexLocations[2]),
color: expectedColor,
},
{
// Triangle {v4, v5, v6}
location: Point2D.getCentroid(VertexLocations[3], VertexLocations[4], VertexLocations[5]),
color: expectedColor,
},
];
}
function getTriangleStripTestLocations(expectedColor) {
// Center of two triangles
return [
{
// Triangle {v2, v3, v4}
location: Point2D.getCentroid(VertexLocations[1], VertexLocations[2], VertexLocations[3]),
color: expectedColor,
},
{
// Triangle {v3, v4, v5}
location: Point2D.getCentroid(VertexLocations[2], VertexLocations[3], VertexLocations[4]),
color: expectedColor,
},
];
}
function generateVertexBuffer(vertexLocations) {
const vertexCoords = new Float32Array(vertexLocations.length * 4);
for (let i = 0; i < vertexLocations.length; i++) {
const point = vertexLocations[i].toNDC();
vertexCoords[i * 4 + 0] = point.x;
vertexCoords[i * 4 + 1] = point.y;
vertexCoords[i * 4 + 2] = point.z;
vertexCoords[i * 4 + 3] = point.w;
}
return vertexCoords;
}
class PrimitiveTopologyTest extends GPUTest {
makeAttachmentTexture() {
return this.device.createTexture({
format: kColorFormat,
size: { width: kRTSize, height: kRTSize, depth: 1 },
usage: GPUTextureUsage.OUTPUT_ATTACHMENT | GPUTextureUsage.COPY_SRC,
});
}
run(primitiveTopology, testLocations, usePrimitiveRestart) {
const colorAttachment = this.makeAttachmentTexture();
// Color load operator will clear color attachment to zero.
const encoder = this.device.createCommandEncoder();
const renderPass = encoder.beginRenderPass({
colorAttachments: [
{
attachment: colorAttachment.createView(),
loadValue: { r: 0.0, g: 0.0, b: 0.0, a: 0.0 },
},
],
});
let indexFormat = undefined;
if (primitiveTopology === 'triangle-strip' || primitiveTopology === 'line-strip') {
indexFormat = 'uint32';
}
// Draw a primitive using 6 vertices based on the type.
// Pixels are generated based on vertex position.
// If point, 1 pixel is generated at each vertex location.
// Otherwise, >1 pixels could be generated.
// Output color is solid green.
renderPass.setPipeline(
this.device.createRenderPipeline({
vertexStage: {
module: this.device.createShaderModule({
code: `
[[location(0)]] var<in> pos : vec4<f32>;
[[builtin(position)]] var<out> Position : vec4<f32>;
[[stage(vertex)]] fn main() -> void {
Position = pos;
return;
}`,
}),
entryPoint: 'main',
},
fragmentStage: {
module: this.device.createShaderModule({
code: `
[[location(0)]] var<out> fragColor : vec4<f32>;
[[stage(fragment)]] fn main() -> void {
fragColor = vec4<f32>(0.0, 1.0, 0.0, 1.0);
return;
}`,
}),
entryPoint: 'main',
},
primitiveTopology,
colorStates: [{ format: kColorFormat }],
vertexState: {
indexFormat,
vertexBuffers: [
{
arrayStride: 4 * Float32Array.BYTES_PER_ELEMENT,
attributes: [
{
format: 'float4',
offset: 0,
shaderLocation: 0,
},
],
},
],
},
})
);
// Create vertices for the primitive in a vertex buffer and bind it.
const vertexCoords = generateVertexBuffer(VertexLocations);
const vertexBuffer = this.makeBufferWithContents(vertexCoords, GPUBufferUsage.VERTEX);
renderPass.setVertexBuffer(0, vertexBuffer);
// Restart the strip between [v3, <restart>, v4].
if (usePrimitiveRestart) {
const indexBuffer = this.makeBufferWithContents(
new Uint32Array([0, 1, 2, -1, 3, 4, 5]),
GPUBufferUsage.INDEX
);
renderPass.setIndexBuffer(indexBuffer, 'uint32');
renderPass.drawIndexed(7); // extra index for restart
} else {
renderPass.draw(6);
}
renderPass.endPass();
this.device.defaultQueue.submit([encoder.finish()]);
for (const testPixel of testLocations) {
this.expectSinglePixelIn2DTexture(
colorAttachment,
kColorFormat,
{ x: testPixel.location.x, y: testPixel.location.y },
{ exp: testPixel.color }
);
}
}
}
export const g = makeTestGroup(PrimitiveTopologyTest);
// Compute test locations for valid and invalid pixels for each topology.
// If the primitive covers the pixel, the color value will be |kValidPixelColor|.
// Otherwise, a non-covered pixel will be |kInvalidPixelColor|.
g.test('point_list').fn(async t => {
// Check valid test locations
const testLocations = getPointTestLocations(kValidPixelColor);
// Check invalid test locations
testLocations.concat(getLineStripTestLocations(kInvalidPixelColor));
testLocations.concat(getTriangleListTestLocations(kInvalidPixelColor));
testLocations.concat(getTriangleStripTestLocations(kInvalidPixelColor));
t.run('point-list', testLocations, /*usePrimitiveRestart=*/ false);
});
g.test('line_list').fn(async t => {
// Check valid test locations
const testLocations = getLineTestLocations(kValidPixelColor);
// Check invalid test locations
testLocations.concat(getLineStripTestLocations(kInvalidPixelColor));
testLocations.concat(getTriangleListTestLocations(kInvalidPixelColor));
testLocations.concat(getTriangleStripTestLocations(kInvalidPixelColor));
t.run('line-list', testLocations, /*usePrimitiveRestart=*/ false);
});
g.test('line_strip').fn(async t => {
// Check valid test locations
const testLocations = getLineTestLocations(kValidPixelColor);
testLocations.concat(getLineStripTestLocations(kValidPixelColor));
// Check invalid test locations
testLocations.concat(getTriangleListTestLocations(kInvalidPixelColor));
testLocations.concat(getTriangleStripTestLocations(kInvalidPixelColor));
t.run('line-strip', testLocations, /*usePrimitiveRestart=*/ false);
});
g.test('line_strip,primitive_restart').fn(async t => {
// Check valid test locations
const testLocations = getPrimitiveRestartLineTestLocations(kValidPixelColor);
testLocations.concat(getLineStripTestLocations(kValidPixelColor));
// Check invalid test locations
testLocations.concat(getTriangleListTestLocations(kInvalidPixelColor));
testLocations.concat(getTriangleStripTestLocations(kInvalidPixelColor));
t.run('line-strip', testLocations, /*usePrimitiveRestart=*/ true);
});
g.test('triangle_list').fn(async t => {
// Check valid test locations
const testLocations = getTriangleListTestLocations(kValidPixelColor);
// Check invalid test locations
testLocations.concat(getTriangleStripTestLocations(kInvalidPixelColor));
t.run('triangle-list', testLocations, /*usePrimitiveRestart=*/ false);
});
g.test('triangle_strip').fn(async t => {
// Check valid test locations
const testLocations = getTriangleListTestLocations(kValidPixelColor);
testLocations.concat(getTriangleStripTestLocations(kValidPixelColor));
t.run('triangle-strip', testLocations, /*usePrimitiveRestart=*/ false);
});
g.test('triangle_strip,primitive_restart').fn(async t => {
// Check valid test locations
const testLocations = getTriangleListTestLocations(kValidPixelColor);
testLocations.concat(getTriangleStripTestLocations(kInvalidPixelColor));
t.run('triangle-strip', testLocations, /*usePrimitiveRestart=*/ true);
});