diff options
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/webgpu/util/copy_to_texture.ts')
-rw-r--r-- | dom/webgpu/tests/cts/checkout/src/webgpu/util/copy_to_texture.ts | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/util/copy_to_texture.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/util/copy_to_texture.ts new file mode 100644 index 0000000000..2f751ff6a2 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/util/copy_to_texture.ts @@ -0,0 +1,194 @@ +import { assert, memcpy } from '../../common/util/util.js'; +import { RegularTextureFormat } from '../capability_info.js'; +import { GPUTest } from '../gpu_test.js'; +import { reifyExtent3D, reifyOrigin3D } from '../util/unions.js'; + +import { makeInPlaceColorConversion } from './color_space_conversion.js'; +import { TexelView } from './texture/texel_view.js'; +import { TexelCompareOptions, textureContentIsOKByT2B } from './texture/texture_ok.js'; + +/** + * Predefined copy sub rect meta infos. + */ +export const kCopySubrectInfo = [ + { + srcOrigin: { x: 2, y: 2 }, + dstOrigin: { x: 0, y: 0, z: 0 }, + srcSize: { width: 16, height: 16 }, + dstSize: { width: 4, height: 4 }, + copyExtent: { width: 4, height: 4, depthOrArrayLayers: 1 }, + }, + { + srcOrigin: { x: 10, y: 2 }, + dstOrigin: { x: 0, y: 0, z: 0 }, + srcSize: { width: 16, height: 16 }, + dstSize: { width: 4, height: 4 }, + copyExtent: { width: 4, height: 4, depthOrArrayLayers: 1 }, + }, + { + srcOrigin: { x: 2, y: 10 }, + dstOrigin: { x: 0, y: 0, z: 0 }, + srcSize: { width: 16, height: 16 }, + dstSize: { width: 4, height: 4 }, + copyExtent: { width: 4, height: 4, depthOrArrayLayers: 1 }, + }, + { + srcOrigin: { x: 10, y: 10 }, + dstOrigin: { x: 0, y: 0, z: 0 }, + srcSize: { width: 16, height: 16 }, + dstSize: { width: 4, height: 4 }, + copyExtent: { width: 4, height: 4, depthOrArrayLayers: 1 }, + }, + { + srcOrigin: { x: 2, y: 2 }, + dstOrigin: { x: 2, y: 2, z: 0 }, + srcSize: { width: 16, height: 16 }, + dstSize: { width: 16, height: 16 }, + copyExtent: { width: 4, height: 4, depthOrArrayLayers: 1 }, + }, + { + srcOrigin: { x: 10, y: 2 }, + dstOrigin: { x: 2, y: 2, z: 0 }, + srcSize: { width: 16, height: 16 }, + dstSize: { width: 16, height: 16 }, + copyExtent: { width: 4, height: 4, depthOrArrayLayers: 1 }, + }, +] as const; + +export class CopyToTextureUtils extends GPUTest { + doFlipY( + sourcePixels: Uint8ClampedArray, + width: number, + height: number, + bytesPerPixel: number + ): Uint8ClampedArray { + const dstPixels = new Uint8ClampedArray(width * height * bytesPerPixel); + for (let i = 0; i < height; ++i) { + for (let j = 0; j < width; ++j) { + const srcPixelPos = i * width + j; + // WebGL readPixel returns pixels from bottom-left origin. Using CopyExternalImageToTexture + // to copy from WebGL Canvas keeps top-left origin. So the expectation from webgl.readPixel should + // be flipped. + const dstPixelPos = (height - i - 1) * width + j; + + memcpy( + { src: sourcePixels, start: srcPixelPos * bytesPerPixel, length: bytesPerPixel }, + { dst: dstPixels, start: dstPixelPos * bytesPerPixel } + ); + } + } + + return dstPixels; + } + + getExpectedDstPixelsFromSrcPixels({ + srcPixels, + srcOrigin, + srcSize, + dstOrigin, + dstSize, + subRectSize, + format, + flipSrcBeforeCopy, + srcDoFlipYDuringCopy, + conversion, + }: { + srcPixels: Uint8ClampedArray; + srcOrigin: GPUOrigin2D; + srcSize: GPUExtent3D; + dstOrigin: GPUOrigin3D; + dstSize: GPUExtent3D; + subRectSize: GPUExtent3D; + format: RegularTextureFormat; + flipSrcBeforeCopy: boolean; + srcDoFlipYDuringCopy: boolean; + conversion: { + srcPremultiplied: boolean; + dstPremultiplied: boolean; + srcColorSpace?: PredefinedColorSpace; + dstColorSpace?: PredefinedColorSpace; + }; + }): TexelView { + const applyConversion = makeInPlaceColorConversion(conversion); + + const reifySrcOrigin = reifyOrigin3D(srcOrigin); + const reifySrcSize = reifyExtent3D(srcSize); + const reifyDstOrigin = reifyOrigin3D(dstOrigin); + const reifyDstSize = reifyExtent3D(dstSize); + const reifySubRectSize = reifyExtent3D(subRectSize); + + assert( + reifyDstOrigin.x + reifySubRectSize.width <= reifyDstSize.width && + reifyDstOrigin.y + reifySubRectSize.height <= reifyDstSize.height, + 'subrect is out of bounds' + ); + + const divide = 255.0; + return TexelView.fromTexelsAsColors( + format, + coords => { + assert( + coords.x >= reifyDstOrigin.x && + coords.y >= reifyDstOrigin.y && + coords.x < reifyDstOrigin.x + reifySubRectSize.width && + coords.y < reifyDstOrigin.y + reifySubRectSize.height && + coords.z === 0, + 'out of bounds' + ); + // Map dst coords to get candidate src pixel position in y. + let yInSubRect = coords.y - reifyDstOrigin.y; + + // If srcDoFlipYDuringCopy is true, a flipY op has been applied to src during copy. + // WebGPU spec requires origin option relative to the top-left corner of the source image, + // increasing downward consistently. + // https://www.w3.org/TR/webgpu/#dom-gpuimagecopyexternalimage-flipy + // Flip only happens in copy rect contents and src origin always top-left. + // Get candidate src pixel position in y by mirroring in copy sub rect. + if (srcDoFlipYDuringCopy) yInSubRect = reifySubRectSize.height - 1 - yInSubRect; + + let src_y = yInSubRect + reifySrcOrigin.y; + + // Test might generate flipped source based on srcPixels, e.g. Create ImageBitmap based on srcPixels but set orientation to 'flipY' + // Get candidate src pixel position in y by mirroring in source. + if (flipSrcBeforeCopy) src_y = reifySrcSize.height - src_y - 1; + + const pixelPos = + src_y * reifySrcSize.width + (coords.x - reifyDstOrigin.x) + reifySrcOrigin.x; + + const rgba = { + R: srcPixels[pixelPos * 4] / divide, + G: srcPixels[pixelPos * 4 + 1] / divide, + B: srcPixels[pixelPos * 4 + 2] / divide, + A: srcPixels[pixelPos * 4 + 3] / divide, + }; + applyConversion(rgba); + return rgba; + }, + { clampToFormatRange: true } + ); + } + + doTestAndCheckResult( + imageCopyExternalImage: GPUImageCopyExternalImage, + dstTextureCopyView: GPUImageCopyTextureTagged, + expTexelView: TexelView, + copySize: Required<GPUExtent3DDict>, + texelCompareOptions: TexelCompareOptions + ): void { + this.device.queue.copyExternalImageToTexture( + imageCopyExternalImage, + dstTextureCopyView, + copySize + ); + + const resultPromise = textureContentIsOKByT2B( + this, + { texture: dstTextureCopyView.texture, origin: dstTextureCopyView.origin }, + copySize, + { expTexelView }, + texelCompareOptions + ); + this.eventualExpectOK(resultPromise); + this.trackForCleanup(dstTextureCopyView.texture); + } +} |