| <!DOCTYPE html> |
| <meta charset="utf-8"> |
| <title>Blitting Texture To Texture</title> |
| <meta name="assert" content="Blit operations populate a texture correctly (all mip levels)."> |
| <link rel="match" href="blit-commands-texture-to-texture-expected.html"> |
| <p>Pass if square canvas below shows a green mip chain on a red background.</p> |
| <canvas width="512" height="512"></canvas> |
| <body> |
| <script src="js/webgpu-functions.js"></script> |
| <script> |
| if (window.testRunner) |
| testRunner.waitUntilDone(); |
| |
| async function loadTextureFromCanvas2d(device, canvas2d) { |
| const textureSize = { |
| width: canvas2d.width, |
| height: canvas2d.height, |
| depth: 2 |
| }; |
| |
| // Mipmap count |
| let maxResolution = Math.max(canvas2d.width, canvas2d.height); |
| let mipLevelCount = Math.ceil(Math.log2(maxResolution)); |
| if (mipLevelCount < 1) mipLevelCount = 1; |
| |
| const textureDescriptor = { |
| size: textureSize, |
| mipLevelCount: mipLevelCount, |
| sampleCount: 1, |
| dimension: "2d", |
| format: "rgba8unorm", |
| usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.COPY_DST | GPUTextureUsage.SAMPLED |
| }; |
| |
| // Create texture and also add the descriptor |
| const texture = device.createTexture(textureDescriptor); |
| textureSize.depth = 1; |
| texture.descriptor = textureDescriptor; |
| |
| // Texture data |
| const context2d = canvas2d.getContext('2d'); |
| const imageData = context2d.getImageData(0, 0, canvas2d.width, canvas2d.height); |
| |
| const textureDataBufferDescriptor = { |
| size: imageData.data.length, |
| usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.MAP_WRITE |
| }; |
| const textureDataBuffer = device.createBuffer(textureDataBufferDescriptor); |
| |
| const textureArrayBuffer = await textureDataBuffer.mapWriteAsync(); |
| const textureWriteArray = new Uint8Array(textureArrayBuffer); |
| textureWriteArray.set(imageData.data); |
| textureDataBuffer.unmap(); |
| |
| const dataCopyView = { |
| buffer: textureDataBuffer, |
| offset: 0, |
| rowPitch: canvas2d.width * 4, |
| imageHeight: canvas2d.width * canvas2d.height * 4 |
| }; |
| const textureCopyView = { |
| texture: texture, |
| mipLevel: 0, |
| arrayLayer: 0, |
| origin: { x: 0, y: 0, z: 0 } |
| }; |
| |
| const blitCommandEncoder = device.createCommandEncoder(); |
| blitCommandEncoder.copyBufferToTexture(dataCopyView, textureCopyView, textureSize); |
| device.getQueue().submit([blitCommandEncoder.finish()]); |
| |
| return texture; |
| } |
| |
| async function test(device) { |
| const canvas2d = await document.querySelector("canvas"); |
| const context2d = canvas2d.getContext('2d'); |
| |
| // textureA (layer 0, mip 0): Green |
| context2d.fillStyle = 'rgb(0, 255, 0)'; |
| context2d.fillRect(0, 0, canvas2d.width, canvas2d.height); |
| const textureA = await loadTextureFromCanvas2d(device, canvas2d); |
| |
| // textureB (layer 0, mip 0): Red |
| context2d.fillStyle = 'rgb(255, 0, 0)'; |
| context2d.fillRect(0, 0, canvas2d.width, canvas2d.height); |
| const textureB = await loadTextureFromCanvas2d(device, canvas2d); |
| |
| // Clean canvas to blue |
| context2d.fillStyle = 'rgb(0, 0, 255)'; |
| context2d.fillRect(0, 0, canvas2d.width, canvas2d.height); |
| |
| // Test copyTextureToTexture |
| |
| let size = Object.assign({}, textureA.descriptor.size); |
| let blitCommandEncoder = device.createCommandEncoder(); |
| for (var i = 1; i < textureA.descriptor.mipLevelCount; ++i) { |
| size.width = Math.max(Math.floor(size.width / 2), 1); |
| size.height = Math.max(Math.floor(size.height / 2), 1); |
| |
| let srcTextureCopyView = { |
| texture: textureA, |
| mipLevel: 0, |
| arrayLayer: 0, |
| origin: { x: 0, y: 0, z: 0 } |
| }; |
| let dstTextureCopyView = { |
| texture: textureA, |
| mipLevel: i, |
| arrayLayer: 1, |
| origin: { x: 0, y: 0, z: 0 } |
| }; |
| // Populate textureA (layer 1, mip i) |
| blitCommandEncoder.copyTextureToTexture(srcTextureCopyView, dstTextureCopyView, size); |
| |
| srcTextureCopyView = { |
| texture: textureA, |
| mipLevel: i, |
| arrayLayer: 1, |
| origin: { x: 0, y: 0, z: 0 } |
| }; |
| dstTextureCopyView = { |
| texture: textureB, |
| mipLevel: 0, |
| arrayLayer: 0, |
| origin: { x: 0, y: size.height, z: 0 } |
| }; |
| // Populate textureB (layer 0, mip 0) with textureA (layer 1, mip i) |
| blitCommandEncoder.copyTextureToTexture(srcTextureCopyView, dstTextureCopyView, size); |
| } |
| |
| // Render textureB into canvas2d |
| |
| const imageDataLengthInBytes = canvas2d.width * canvas2d.height * 4; |
| const bufferDescriptor = { |
| size: imageDataLengthInBytes, |
| usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ |
| }; |
| const bufferA = device.createBuffer(bufferDescriptor); |
| |
| const bufferCopyView = { |
| buffer: bufferA, |
| rowPitch: canvas2d.width * 4, |
| imageHeight: canvas2d.width * canvas2d.height * 4 |
| }; |
| const textureCopyView = { |
| texture: textureB |
| }; |
| blitCommandEncoder.copyTextureToBuffer(textureCopyView, bufferCopyView, textureA.descriptor.size); |
| |
| device.getQueue().submit([blitCommandEncoder.finish()]); |
| |
| const ab = await bufferA.mapReadAsync() |
| const array = new Uint8ClampedArray(ab); |
| const resultImageData = new ImageData(array, canvas2d.width, canvas2d.height); |
| |
| context2d.putImageData(resultImageData, 0, 0); |
| |
| bufferA.destroy(); |
| } |
| |
| getBasicDevice().then(function(device) { |
| test(device).then(function() { |
| if (window.testRunner) |
| testRunner.notifyDone(); |
| }, function(e) { |
| if (window.testRunner) |
| testRunner.notifyDone(); |
| }); |
| }, function() { |
| const canvas2d = document.querySelector("canvas"); |
| drawGreenMipChainOnRedBackgroundInSoftware(canvas2d); |
| if (window.testRunner) |
| testRunner.notifyDone(); |
| }); |
| </script> |
| </body> |