Roll WebGPU CTS
Roll in copyImageBitmapToTexture cts
Change-Id: Ie4ddf5f7dad5fa6487cbaedc5ca78e6e1d448602
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1986630
Commit-Queue: Kai Ninomiya <kainino@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Cr-Commit-Position: refs/heads/master@{#729235}
diff --git a/webgpu/cts.html b/webgpu/cts.html
index 6099f73..5c529e9 100644
--- a/webgpu/cts.html
+++ b/webgpu/cts.html
@@ -66,6 +66,7 @@
<meta name=variant content='?q=cts:command_buffer/render/basic:'>
<meta name=variant content='?q=cts:command_buffer/render/rendering:'>
<meta name=variant content='?q=cts:command_buffer/render/storeop:'>
+<meta name=variant content='?q=cts:copyImageBitmapToTexture:'>
<meta name=variant content='?q=cts:examples:'>
<meta name=variant content='?q=cts:fences:'>
<meta name=variant content='?q=cts:resource_init/sampled_texture_clear:'>
diff --git a/webgpu/framework/version.js b/webgpu/framework/version.js
index 9d1c081..8dd8302 100644
--- a/webgpu/framework/version.js
+++ b/webgpu/framework/version.js
@@ -1,3 +1,3 @@
// AUTO-GENERATED - DO NOT EDIT. See tools/gen_version.
-export const version = '70754155a0bca2c24e3e3c249bab9ee3dd765a26';
+export const version = 'a5c59f8995f38843e332ff4d869a72f75d2f028d';
diff --git a/webgpu/suites/cts/copyImageBitmapToTexture.spec.js b/webgpu/suites/cts/copyImageBitmapToTexture.spec.js
new file mode 100644
index 0000000..c9b07e8
--- /dev/null
+++ b/webgpu/suites/cts/copyImageBitmapToTexture.spec.js
@@ -0,0 +1,148 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+export const description = `
+copy imageBitmap To texture tests.
+`;
+import { TestGroup, pcombine, poptions } from '../../framework/index.js';
+import { GPUTest } from './gpu_test.js';
+
+function calculateRowPitch(width, bytesPerPixel) {
+ const bytesPerRow = width * bytesPerPixel; // Rounds up to a multiple of 256 according to WebGPU requirements.
+
+ return (bytesPerRow - 1 >> 8) + 1 << 8;
+}
+
+class F extends GPUTest {
+ checkCopyImageBitmapResult(src, expected, width, height, bytesPerPixel) {
+ const exp = new Uint8Array(expected.buffer, expected.byteOffset, expected.byteLength);
+ const rowPitch = calculateRowPitch(width, bytesPerPixel);
+ const dst = this.createCopyForMapRead(src, rowPitch * height);
+ this.eventualAsyncExpectation(async niceStack => {
+ const actual = new Uint8Array((await dst.mapReadAsync()));
+ const check = this.checkBufferWithRowPitch(actual, exp, width, height, rowPitch, bytesPerPixel);
+
+ if (check !== undefined) {
+ niceStack.message = check;
+ this.rec.fail(niceStack);
+ }
+
+ dst.destroy();
+ });
+ }
+
+ checkBufferWithRowPitch(actual, exp, width, height, rowPitch, bytesPerPixel) {
+ const lines = [];
+ let failedPixels = 0;
+
+ for (let i = 0; i < height; ++i) {
+ const bytesPerRow = width * bytesPerPixel;
+
+ for (let j = 0; j < bytesPerRow; ++j) {
+ const indexExp = j + i * bytesPerRow;
+ const indexActual = j + rowPitch * i;
+
+ if (actual[indexActual] !== exp[indexExp]) {
+ if (failedPixels > 4) {
+ break;
+ }
+
+ failedPixels++;
+ lines.push(`at [${indexExp}], expected ${exp[indexExp]}, got ${actual[indexActual]}`);
+ }
+ }
+
+ if (failedPixels > 4) {
+ lines.push('... and more');
+ break;
+ }
+ }
+
+ return failedPixels > 0 ? lines.join('\n') : undefined;
+ } // Using drawImage to extract imageBitmap content.
+
+
+ imageBitmapToData(imageBitmap) {
+ const imageCanvas = document.createElement('canvas');
+ imageCanvas.width = imageBitmap.width;
+ imageCanvas.height = imageBitmap.height;
+ const imageCanvasContext = imageCanvas.getContext('2d');
+
+ if (!imageCanvasContext) {
+ throw new Error('Cannot create canvas context for reading back contents from imageBitmap.');
+ }
+
+ imageCanvasContext.drawImage(imageBitmap, 0, 0, imageBitmap.width, imageBitmap.height);
+ return imageCanvasContext.getImageData(0, 0, imageBitmap.width, imageBitmap.height).data;
+ }
+
+}
+
+export const g = new TestGroup(F);
+g.test('from ImageData', async t => {
+ const {
+ width,
+ height
+ } = t.params; // The texture format is rgba8uint, so the bytes per pixel is 4.
+
+ const bytesPerPixel = 4;
+ const imagePixels = new Uint8ClampedArray(bytesPerPixel * width * height);
+
+ for (let i = 0; i < width * height * bytesPerPixel; ++i) {
+ imagePixels[i] = i % 4 === 3 ? 255 : i % 256;
+ }
+
+ const imageData = new ImageData(imagePixels, width, height);
+ const imageBitmap = await createImageBitmap(imageData);
+ const dst = t.device.createTexture({
+ size: {
+ width: imageBitmap.width,
+ height: imageBitmap.height,
+ depth: 1
+ },
+ format: 'rgba8uint',
+ usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.COPY_SRC
+ });
+ t.device.defaultQueue.copyImageBitmapToTexture({
+ imageBitmap,
+ origin: {
+ x: 0,
+ y: 0
+ }
+ }, {
+ texture: dst
+ }, {
+ width: imageBitmap.width,
+ height: imageBitmap.height,
+ depth: 1
+ });
+ const data = t.imageBitmapToData(imageBitmap);
+ const rowPitchValue = calculateRowPitch(imageBitmap.width, bytesPerPixel);
+ const testBuffer = t.device.createBuffer({
+ size: rowPitchValue * imageBitmap.height,
+ usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST
+ });
+ const encoder = t.device.createCommandEncoder();
+ encoder.copyTextureToBuffer({
+ texture: dst,
+ mipLevel: 0,
+ origin: {
+ x: 0,
+ y: 0,
+ z: 0
+ }
+ }, {
+ buffer: testBuffer,
+ rowPitch: rowPitchValue,
+ imageHeight: 0
+ }, {
+ width: imageBitmap.width,
+ height: imageBitmap.height,
+ depth: 1
+ });
+ t.device.defaultQueue.submit([encoder.finish()]);
+ t.checkCopyImageBitmapResult(testBuffer, data, imageBitmap.width, imageBitmap.height, bytesPerPixel);
+}).params(pcombine(poptions('width', [1, 2, 4, 15, 255, 256]), //
+poptions('height', [1, 2, 4, 15, 255, 256])));
+//# sourceMappingURL=copyImageBitmapToTexture.spec.js.map
\ No newline at end of file
diff --git a/webgpu/suites/cts/gpu_test.js b/webgpu/suites/cts/gpu_test.js
index 67429b9..cf52a6c 100644
--- a/webgpu/suites/cts/gpu_test.js
+++ b/webgpu/suites/cts/gpu_test.js
@@ -99,19 +99,23 @@
return this.device.createShaderModule({
code
});
- } // TODO: add an expectContents for textures, which logs data: uris on failure
+ }
-
- expectContents(src, expected) {
- const exp = new Uint8Array(expected.buffer, expected.byteOffset, expected.byteLength);
- const size = expected.buffer.byteLength;
+ createCopyForMapRead(src, size) {
const dst = this.device.createBuffer({
- size: expected.buffer.byteLength,
+ size,
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST
});
const c = this.device.createCommandEncoder();
c.copyBufferToBuffer(src, 0, dst, 0, size);
this.queue.submit([c.finish()]);
+ return dst;
+ } // TODO: add an expectContents for textures, which logs data: uris on failure
+
+
+ expectContents(src, expected) {
+ const exp = new Uint8Array(expected.buffer, expected.byteOffset, expected.byteLength);
+ const dst = this.createCopyForMapRead(src, expected.buffer.byteLength);
this.eventualAsyncExpectation(async niceStack => {
const actual = new Uint8Array((await dst.mapReadAsync()));
const check = this.checkBuffer(actual, exp);
@@ -153,7 +157,21 @@
failedPixels++;
lines.push(`at [${i}], expected ${exp[i]}, got ${actual[i]}`);
}
- }
+ } // TODO: Could make a more convenient message, which could look like e.g.:
+ //
+ // Starting at offset 48,
+ // got 22222222 ABCDABCD 99999999
+ // but expected 22222222 55555555 99999999
+ //
+ // or
+ //
+ // Starting at offset 0,
+ // got 00000000 00000000 00000000 00000000 (... more)
+ // but expected 00FF00FF 00FF00FF 00FF00FF 00FF00FF (... more)
+ //
+ // Or, maybe these diffs aren't actually very useful (given we have the prints just above here),
+ // and we should remove them. More important will be logging of texture data in a visual format.
+
if (size <= 256 && failedPixels > 0) {
const expHex = Array.from(exp).map(x => x.toString(16).padStart(2, '0')).join('');
diff --git a/webgpu/suites/cts/index.js b/webgpu/suites/cts/index.js
index 9fb8395..e566792 100644
--- a/webgpu/suites/cts/index.js
+++ b/webgpu/suites/cts/index.js
@@ -54,6 +54,10 @@
"description": "renderPass store op test that drawn quad is either stored or cleared based on storeop"
},
{
+ "path": "copyImageBitmapToTexture",
+ "description": "copy imageBitmap To texture tests."
+ },
+ {
"path": "examples",
"description": "Examples of writing CTS tests with various features.\n\nStart here when looking for examples of basic framework usage."
},