| export const description = 'copyBufferToBuffer operation tests'; |
| |
| import { makeTestGroup } from '../../../../common/framework/test_group.js'; |
| import { assert } from '../../../../common/util/util.js'; |
| import { AllFeaturesMaxLimitsGPUTest } from '../../../gpu_test.js'; |
| |
| export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest); |
| |
| g.test('single') |
| .desc( |
| `Validate the correctness of the copy by filling the srcBuffer with testable data, doing |
| CopyBufferToBuffer() copy, and verifying the content of the whole dstBuffer with MapRead: |
| Copy {4 bytes, part of, the whole} srcBuffer to the dstBuffer {with, without} a non-zero valid |
| srcOffset that |
| - covers the whole dstBuffer |
| - covers the beginning of the dstBuffer |
| - covers the end of the dstBuffer |
| - covers neither the beginning nor the end of the dstBuffer` |
| ) |
| .params(u => |
| u // |
| // Whether the test case requires the newly-added method signature or not. |
| .combine('newSig', [false, true]) |
| .beginSubcases() |
| .combine('srcOffset', [0, 4, 8, 16, undefined]) |
| .combine('dstOffset', [0, 4, 8, 16, undefined]) |
| .unless( |
| p => (p.srcOffset === undefined || p.dstOffset === undefined) && p.srcOffset !== p.dstOffset |
| ) |
| .combine('copySize', [0, 4, 8, 16, undefined]) |
| .expand('srcBufferSize', p => [ |
| (p.srcOffset ?? 0) + (p.copySize ?? 0), |
| (p.srcOffset ?? 0) + (p.copySize ?? 0) + 8, |
| ]) |
| .expand('dstBufferSize', p => [ |
| (p.dstOffset ?? 0) + (p.copySize ?? 0), |
| (p.dstOffset ?? 0) + (p.copySize ?? 0) + 8, |
| ]) |
| // Bifurcate the cases between newSig=false and newSig=true based on whether they need it. |
| .filter(p => { |
| const needsNewSignature = [p.srcOffset, p.dstOffset, p.copySize].includes(undefined); |
| return p.newSig === needsNewSignature; |
| }) |
| ) |
| .fn(t => { |
| const { srcOffset, dstOffset, copySize, srcBufferSize, dstBufferSize } = t.params; |
| |
| const srcData = new Uint8Array(srcBufferSize); |
| for (let i = 0; i < srcBufferSize; ++i) { |
| srcData[i] = i + 1; |
| } |
| |
| const src = t.makeBufferWithContents(srcData, GPUBufferUsage.COPY_SRC); |
| |
| const dst = t.createBufferTracked({ |
| size: dstBufferSize, |
| usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST, |
| }); |
| |
| const encoder = t.device.createCommandEncoder(); |
| if (srcOffset === undefined || dstOffset === undefined) { |
| assert(srcOffset === undefined && dstOffset === undefined); |
| encoder.copyBufferToBuffer(src, dst, copySize); |
| } else { |
| encoder.copyBufferToBuffer(src, srcOffset, dst, dstOffset, copySize); |
| } |
| |
| const expectedSrcOffset = srcOffset ?? 0; |
| const expectedDstOffset = dstOffset ?? 0; |
| const expectedCopySize = copySize ?? srcBufferSize - expectedSrcOffset; |
| |
| const isValid = dstBufferSize - expectedDstOffset >= expectedCopySize; |
| t.expectValidationError(() => { |
| t.device.queue.submit([encoder.finish()]); |
| }, !isValid); |
| |
| const expectedDstData = new Uint8Array(dstBufferSize); |
| for (let i = 0; i < expectedCopySize; ++i) { |
| expectedDstData[expectedDstOffset + i] = srcData[expectedSrcOffset + i]; |
| } |
| |
| t.expectGPUBufferValuesEqual(dst, expectedDstData); |
| }); |
| |
| g.test('state_transitions') |
| .desc( |
| `Test proper state transitions/barriers happen between copy commands. |
| Copy part of src to dst, then a different part of dst to src, and check contents of both.` |
| ) |
| .fn(t => { |
| const srcData = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); |
| const dstData = new Uint8Array([10, 20, 30, 40, 50, 60, 70, 80]); |
| |
| const src = t.makeBufferWithContents( |
| srcData, |
| GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST |
| ); |
| const dst = t.makeBufferWithContents( |
| dstData, |
| GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST |
| ); |
| |
| const encoder = t.device.createCommandEncoder(); |
| encoder.copyBufferToBuffer(src, 0, dst, 4, 4); |
| encoder.copyBufferToBuffer(dst, 0, src, 4, 4); |
| t.device.queue.submit([encoder.finish()]); |
| |
| const expectedSrcData = new Uint8Array([1, 2, 3, 4, 10, 20, 30, 40]); |
| const expectedDstData = new Uint8Array([10, 20, 30, 40, 1, 2, 3, 4]); |
| t.expectGPUBufferValuesEqual(src, expectedSrcData); |
| t.expectGPUBufferValuesEqual(dst, expectedDstData); |
| }); |
| |
| g.test('copy_order') |
| .desc( |
| `Test copy commands in one command buffer occur in the correct order. |
| First copies one region from src to dst, then another region from src to an overlapping region |
| of dst, then checks the dst buffer's contents.` |
| ) |
| .fn(t => { |
| const srcData = new Uint32Array([1, 2, 3, 4, 5, 6, 7, 8]); |
| |
| const src = t.makeBufferWithContents(srcData, GPUBufferUsage.COPY_SRC); |
| |
| const dst = t.createBufferTracked({ |
| size: srcData.length * 4, |
| usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST, |
| }); |
| |
| const encoder = t.device.createCommandEncoder(); |
| encoder.copyBufferToBuffer(src, 0, dst, 0, 16); |
| encoder.copyBufferToBuffer(src, 16, dst, 8, 16); |
| t.device.queue.submit([encoder.finish()]); |
| |
| const expectedDstData = new Uint32Array([1, 2, 5, 6, 7, 8, 0, 0]); |
| t.expectGPUBufferValuesEqual(dst, expectedDstData); |
| }); |