2.4 Tu primer programa WebGPU (Hello Compute)
El primer programa será un ejemplo de cálculo simple. Creará una matriz de datos, la enviará a la GPU y luego la volverá a leer. Después de leerla, la escribirá en la ventana de la consola (verifique que los valores sean los que deberían ser).
(async () => {
if (!navigator.gpu) {
console.log(
"WebGPU is not supported. Enable chrome://flags/#enable-unsafe-webgpu flag."
);
return;
}
const adapter = await navigator.gpu.requestAdapter();
if (!adapter) {
console.log("Failed to get GPU adapter.");
return;
}
const device = await adapter.requestDevice();
// Get a GPU buffer in a mapped state and and arrayBuffer for writing.
const gpuWriteBuffer = device.createBuffer({
mappedAtCreation: true,
size: 4,
usage: GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC
});
const arrayBuffer = gpuWriteBuffer.getMappedRange();
// Write bytes to buffer.
new Unit8Array(arrayBuffer).set([0, 1, 2, 3]);
// 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({
mappedAtCreation: false,
size: 4,
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
});
// Encode commands for copying buffer to buffer.
const copyEncoder = device.createCommandEncoder();
copyEncoder.copyBufferToBuffer(
gpuWriteBuffer, // source buffer
O, // source offset
gpuReadBuffer, // destination buffer
O, // destination offset
4 // size
);
// Submit copy commands.
const copyCommands = copyEncoder.finish();
device.queue.submit([copyCommands]);
// Read buffer.
await gpuReadBuffer.mapAsync(GPUMapMode.READ);
const copyArrayBuffer = gpuReadBuffer.getMappedRange();
console.log(new Uint8Array(copyArrayBuffer));
})();Algunos métodos de la API WebGPU tardan un tiempo en completarse, por lo que la API admite metodologías asincrónicas. Los métodos que no se completan instantáneamente devuelven una “Promesa” que se puede utilizar para esperar un evento de finalización. Para gestionar este comportamiento asincrónico y facilitar su comprensión, utilice las funciones “async” y “await”.
Comprobación estándar de WebGPU (si la API está disponible).
Obtienes un adaptador y llamas a navigator.gpu.requestAdapter(). requestAdapter() nunca falla, pero puede resolverse como null si no se puede encontrar un adaptador. Un adaptador puede dejar de estar disponible si se desconecta del sistema, se desactiva para ahorrar energía o se marca como “obsoleto”.
Obtendrás un dispositivo lógico llamando a adapted.requestDevice().
GPUDevice proporciona API para crear objetos de GPU, como buffers y texturas, y ejecutar comandos en el dispositivo. WebGPU separa el concepto de dispositivos físicos y lógicos. Un dispositivo físico generalmente representa una única implementación completa (excluyendo la funcionalidad a nivel de instancia), de la cual hay un número finito. Un dispositivo lógico representa una instancia de esa implementación con su propio estado y recursos independientes de otros dispositivos lógicos. Los dispositivos físicos están representados por los controladores GPUAdapter.
Crea un buffer en la GPU.
Copiar datos al búfer en la GPU.
Crea un segundo búfer vacío en la GPU.
Configurar un comando en la GPU para realizar una operación.
Agregue el comando para copiar de un buffer a otro (en la GPU).
Emite el comando para ejecutar los comandos apilados (realiza la tarea). La cola te permite enviar trabajo de forma asincrónica a la GPU. En este momento (versión actual de la API), solo puedes acceder a una única cola predeterminada desde un GPUDevice determinado (sin embargo, esto está sujeto a cambios en futuras actualizaciones).
Copie los datos del segundo búfer al programa cliente e imprímalos en la consola.
La implementación puede no parecer muy útil, pero ha demostrado que su dispositivo está disponible y puede asignar un bloque de memoria en la GPU para luego mover los datos. Como verá más adelante, estos búferes en la GPU se pueden pasar entre los canales de procesamiento y gráficos para almacenar o proporcionar datos para los cálculos.