Skip to content

Commit

Permalink
Merge pull request #1434 from cloudflare/webgpu_cont5
Browse files Browse the repository at this point in the history
  • Loading branch information
edevil authored Nov 22, 2023
2 parents d2a4dd5 + 70e4855 commit fe10ee1
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 12 deletions.
56 changes: 50 additions & 6 deletions src/workerd/api/gpu/gpu-command-encoder.c++
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ GPUCommandEncoder::finish(jsg::Optional<GPUCommandBufferDescriptor> descriptor)
return jsg::alloc<GPUCommandBuffer>(kj::mv(buffer));
};

void GPUCommandEncoder::copyTextureToBuffer(GPUImageCopyTexture source,
GPUImageCopyBuffer destination, GPUExtent3D copySize) {
wgpu::ImageCopyTexture parseGPUImageCopyTexture(GPUImageCopyTexture source) {
wgpu::ImageCopyTexture src{};
src.texture = *source.texture;

Expand Down Expand Up @@ -55,6 +54,10 @@ void GPUCommandEncoder::copyTextureToBuffer(GPUImageCopyTexture source,
src.aspect = parseTextureAspect(aspect);
}

return kj::mv(src);
}

wgpu::ImageCopyBuffer parseGPUImageCopyBuffer(GPUImageCopyBuffer destination) {
wgpu::ImageCopyBuffer dst{};
dst.buffer = *destination.buffer;

Expand All @@ -68,6 +71,10 @@ void GPUCommandEncoder::copyTextureToBuffer(GPUImageCopyTexture source,
dst.layout.rowsPerImage = rowsPerImage;
}

return kj::mv(dst);
}

wgpu::Extent3D parseGPUExtent3D(GPUExtent3D copySize) {
wgpu::Extent3D size{};
KJ_SWITCH_ONEOF(copySize) {
KJ_CASE_ONEOF(coords, jsg::Sequence<GPUIntegerCoordinate>) {
Expand All @@ -88,17 +95,46 @@ void GPUCommandEncoder::copyTextureToBuffer(GPUImageCopyTexture source,
JSG_FAIL_REQUIRE(TypeError, "invalid value for GPUExtent3D");
}
}
KJ_CASE_ONEOF(size, GPUExtent3DDict) {
KJ_IF_SOME(depthOrArrayLayers, size.depthOrArrayLayers) {
KJ_CASE_ONEOF(someSize, GPUExtent3DDict) {
KJ_IF_SOME(depthOrArrayLayers, someSize.depthOrArrayLayers) {
size.depthOrArrayLayers = depthOrArrayLayers;
}
KJ_IF_SOME(height, size.height) {
KJ_IF_SOME(height, someSize.height) {
size.height = height;
}
size.width = size.width;
size.width = someSize.width;
}
}

return kj::mv(size);
}

void GPUCommandEncoder::copyTextureToTexture(GPUImageCopyTexture source,
GPUImageCopyTexture destination,
GPUExtent3D copySize) {
wgpu::ImageCopyTexture src = parseGPUImageCopyTexture(kj::mv(source));
wgpu::ImageCopyTexture dst = parseGPUImageCopyTexture(kj::mv(destination));
wgpu::Extent3D size = parseGPUExtent3D(kj::mv(copySize));

encoder_.CopyTextureToTexture(&src, &dst, &size);
}

void GPUCommandEncoder::copyBufferToTexture(GPUImageCopyBuffer source,
GPUImageCopyTexture destination, GPUExtent3D copySize) {

wgpu::ImageCopyBuffer src = parseGPUImageCopyBuffer(kj::mv(source));
wgpu::ImageCopyTexture dst = parseGPUImageCopyTexture(kj::mv(destination));
wgpu::Extent3D size = parseGPUExtent3D(kj::mv(copySize));

encoder_.CopyBufferToTexture(&src, &dst, &size);
}

void GPUCommandEncoder::copyTextureToBuffer(GPUImageCopyTexture source,
GPUImageCopyBuffer destination, GPUExtent3D copySize) {
wgpu::ImageCopyTexture src = parseGPUImageCopyTexture(kj::mv(source));
wgpu::ImageCopyBuffer dst = parseGPUImageCopyBuffer(kj::mv(destination));
wgpu::Extent3D size = parseGPUExtent3D(kj::mv(copySize));

encoder_.CopyTextureToBuffer(&src, &dst, &size);
}

Expand All @@ -109,6 +145,14 @@ void GPUCommandEncoder::copyBufferToBuffer(jsg::Ref<GPUBuffer> source, GPUSize64
encoder_.CopyBufferToBuffer(*source, sourceOffset, *destination, destinationOffset, size);
};

void GPUCommandEncoder::clearBuffer(jsg::Ref<GPUBuffer> buffer, jsg::Optional<GPUSize64> offset,
jsg::Optional<GPUSize64> size) {
uint64_t o = offset.orDefault(0);
uint64_t s = size.orDefault(wgpu::kWholeSize);

encoder_.ClearBuffer(*buffer, o, s);
}

jsg::Ref<GPURenderPassEncoder>
GPUCommandEncoder::beginRenderPass(GPURenderPassDescriptor descriptor) {

Expand Down
9 changes: 9 additions & 0 deletions src/workerd/api/gpu/gpu-command-encoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ class GPUCommandEncoder : public jsg::Object {
JSG_METHOD(copyBufferToBuffer);
JSG_METHOD(finish);
JSG_METHOD(copyTextureToBuffer);
JSG_METHOD(copyBufferToTexture);
JSG_METHOD(copyTextureToTexture);
JSG_METHOD(clearBuffer);
}

private:
Expand All @@ -66,6 +69,12 @@ class GPUCommandEncoder : public jsg::Object {
GPUSize64 size);
void copyTextureToBuffer(GPUImageCopyTexture source, GPUImageCopyBuffer destination,
GPUExtent3D copySize);
void copyBufferToTexture(GPUImageCopyBuffer source, GPUImageCopyTexture destination,
GPUExtent3D copySize);
void copyTextureToTexture(GPUImageCopyTexture source, GPUImageCopyTexture destination,
GPUExtent3D copySize);
void clearBuffer(jsg::Ref<GPUBuffer> buffer, jsg::Optional<GPUSize64> offset,
jsg::Optional<GPUSize64> size);
};

struct GPUCommandEncoderDescriptor {
Expand Down
87 changes: 81 additions & 6 deletions src/workerd/api/gpu/webgpu-buffer-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,21 @@ export class DurableObjectExample {
ok(device);

// Get a GPU buffer in a mapped state and an arrayBuffer for writing.
const bufferSize = 16384;
const bufferContents = Array.from(
{ length: bufferSize },
(_, index) => index
);
const emptyBufferContents = Array(bufferSize).fill(0);
const gpuWriteBuffer = device.createBuffer({
label: "gpuWriteBuffer",
mappedAtCreation: true,
size: 4,
size: bufferSize,
usage: GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC,
});
ok(gpuWriteBuffer);

deepEqual(gpuWriteBuffer.size, 4n);
deepEqual(gpuWriteBuffer.size, BigInt(bufferSize));

deepEqual(gpuWriteBuffer.usage, 6);

Expand All @@ -33,15 +40,16 @@ export class DurableObjectExample {
ok(arrayBuffer);

// Write bytes to buffer.
new Uint8Array(arrayBuffer).set([0, 1, 2, 3]);
new Uint8Array(arrayBuffer).set(bufferContents);

// Unmap buffer so that it can be used later for copy.
gpuWriteBuffer.unmap();

// Get a GPU buffer for reading in an unmapped state.
const gpuReadBuffer = device.createBuffer({
label: "gpuReadBuffer",
mappedAtCreation: false,
size: 4,
size: bufferSize,
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
});
ok(gpuReadBuffer);
Expand All @@ -54,7 +62,7 @@ export class DurableObjectExample {
0 /* source offset */,
gpuReadBuffer /* destination buffer */,
0 /* destination offset */,
4 /* size */
bufferSize
);

// Submit copy commands.
Expand All @@ -67,7 +75,74 @@ export class DurableObjectExample {
const copyArrayBuffer = gpuReadBuffer.getMappedRange();
ok(copyArrayBuffer);

deepEqual(new Uint8Array(copyArrayBuffer), new Uint8Array([0, 1, 2, 3]));
deepEqual(new Uint8Array(copyArrayBuffer), new Uint8Array(bufferContents));
gpuReadBuffer.unmap();

// Clear buffer
const clearEncoder = device.createCommandEncoder();
clearEncoder.clearBuffer(gpuReadBuffer);
const clearCommands = clearEncoder.finish();
ok(clearCommands);
device.queue.submit([clearCommands]);

// Read cleared buffer.
await gpuReadBuffer.mapAsync(GPUMapMode.READ);
const copyClearedArrayBuffer = gpuReadBuffer.getMappedRange();
ok(copyClearedArrayBuffer);

deepEqual(
new Uint8Array(copyClearedArrayBuffer),
new Uint8Array(emptyBufferContents)
);
gpuReadBuffer.unmap();

const textureSize = 64;
const textureDesc = {
size: { width: textureSize, height: textureSize, depthOrArrayLayers: 1 },
format: "rgba8unorm-srgb",
usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.COPY_DST,
};
const texture = device.createTexture(textureDesc);
const copyTexture = device.createTexture(textureDesc);

// Copy textures
const u32Size = 4;
const textureEncoder = device.createCommandEncoder();
textureEncoder.copyBufferToTexture(
{
buffer: gpuWriteBuffer,
bytesPerRow: u32Size * textureSize,
rowsPerImage: textureSize,
},
{ texture: texture },
textureDesc.size
);
textureEncoder.copyTextureToTexture(
{ texture: texture },
{ texture: copyTexture },
textureDesc.size
);
textureEncoder.copyTextureToBuffer(
{ texture: copyTexture },
{
buffer: gpuReadBuffer,
bytesPerRow: u32Size * textureSize,
rowsPerImage: textureSize,
},
textureDesc.size
);
const textureCommands = textureEncoder.finish();
device.queue.submit([textureCommands]);

// Read cleared buffer.
await gpuReadBuffer.mapAsync(GPUMapMode.READ);
const copyTextureArrayBuffer = gpuReadBuffer.getMappedRange();

deepEqual(
new Uint8Array(copyTextureArrayBuffer),
new Uint8Array(bufferContents)
);
gpuReadBuffer.unmap();

device.queue.writeBuffer(
gpuWriteBuffer,
Expand Down
14 changes: 14 additions & 0 deletions src/workerd/api/gpu/webgpu-windowless-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ import { ok, deepEqual, equal } from "node:assert";
// run manually for now
// bazel run --//src/workerd/io:enable_experimental_webgpu //src/workerd/server:workerd -- test `realpath ./src/workerd/api/gpu/webgpu-windowless-test.gpu-wd-test` --verbose --experimental

async function hash(data) {
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray
.map((bytes) => bytes.toString(16).padStart(2, "0"))
.join("");
return hashHex;
}

export class DurableObjectExample {
constructor(state) {
this.state = state;
Expand Down Expand Up @@ -150,6 +159,11 @@ export class DurableObjectExample {

const data = outputBuffer.getMappedRange();
ok(data);
const result = await hash(data);
equal(
result,
"dd7fd0917e7f9383fd7f2ae5027bf6a4f8f90b2ab5c69c52d4f29a856bd9165a"
);
outputBuffer.unmap();

return new Response("OK");
Expand Down

0 comments on commit fe10ee1

Please sign in to comment.