-
Notifications
You must be signed in to change notification settings - Fork 345
Closed
Description
This is Apple's proposal for GPU to CPU transfers, and visa versa.
We believe that for a first version (MVP), we can stick to an extremely simple model. If we later discover we need something more complicated for efficiency, we can add to the API.
partial interface HostAccessPass {
Promise<ArrayBuffer> downloadData(GPUBuffer buffer, UnsignedLong offset, UnsignedLong length);
void uploadData(GPUBuffer buffer, ArrayBuffer input, UnsignedLong offset);
}
Benefits
- Asynchronous: It is impossible to synchronously read from a buffer, and therefore cause a GPU flush.
- Portable: There is no ambiguity when the site's Javascript can request data to be downloaded or uploaded. (And it's not stateful.)
- Well-defined: It is impossible to use this API to cause a data race between the CPU and GPU. Transfers will only ever occur when both the CPU and GPU are ready for them to occur.
- Secure: ArrayBuffer automatically handles the situation of reading out of bounds.
- Simple: Downloading and uploading are each a single easily-understandable call.
- Implementable: Implementations which don't support mapping work naturally.
- Optimizable: Web content doesn't need to have a special path for UMA vs discrete GPU scenarios, or have to know about how some buffers are CPU accessible but slow on the GPU but others are not CPU accessible but fast on the GPU. The implementation is more likely than the web app to handle all the cases in the most optimized way possible. (Write once, run anywhere.)
- Easy to use: It's likely that any website code using this API will be correct. It's difficult (impossible?) to use this API wrong.
- Style: The rest of the Web platform uses Promises and ArrayBuffers, and this API is no exception.
Drawbacks
All transfers require at least one copy.
Example
function performAsynchronousMath(gpuQueue, gpuBuffer, inputBuffer) {
let uploadPass = queue.createHostAccessPass();
uploadPass.uploadData(gpuBuffer, inputBuffer, 0);
let computePass = queue.createComputePass();
computePass.setState(...);
computePass.setBuffer(buffer, ...);
computePass.dispatch(...);
let downloadPass = queue.createHostAccessPass();
downloadPass.downloadData(buffer, 0, buffer.getLength()).then(function(arrayBuffer) {
let typedArray = new Float32Array(arrayBuffer);
for (let i = 0; i < buffer.getLength() / Float32Array.BYTES_PER_ELEMENT; ++i) {
console.log(String(arrayBuffer[i]));
}
});
queue.enqueue(uploadPass);
queue.enqueue(computePass);
queue.enqueue(downloadPass);
}
kvark
Metadata
Metadata
Assignees
Labels
No labels