Como desarrollador de WebGL, es posible que te sientas intimidado y emocionado por comenzar a usar WebGPU, el sucesor de WebGL que lleva los avances de las APIs de gráficos modernas a la Web.
Es tranquilizador saber que WebGL y WebGPU comparten muchos conceptos básicos. Ambas APIs te permiten ejecutar pequeños programas llamados sombreadores en la GPU. WebGL admite sombreadores de vértices y de fragmentos, mientras que WebGPU también admite sombreadores de cómputos. WebGL usa el lenguaje de sombreado OpenGL (GLSL), mientras que WebGPU usa el lenguaje de sombreado WebGPU (WGSL). Aunque ambos lenguajes son diferentes, los conceptos subyacentes son mayormente los mismos.
Con eso en mente, en este artículo, se destacan algunas diferencias entre WebGL y WebGPU para ayudarte a comenzar.
Estado global
WebGL tiene mucho estado global. Algunas configuraciones se aplican a todas las operaciones de renderización, como las texturas y los búferes que están vinculados. Para establecer este estado global, debes llamar a varias funciones de la API, y permanecerá vigente hasta que lo cambies. El estado global en WebGL es una gran fuente de errores, ya que es fácil olvidarse de cambiar un parámetro de configuración global. Además, el estado global dificulta el uso compartido de código, ya que los desarrolladores deben tener cuidado de no cambiar accidentalmente el estado global de una manera que afecte otras partes del código.
WebGPU es una API sin estado y no mantiene un estado global. En su lugar, usa el concepto de una canalización para encapsular todo el estado de renderización que era global en WebGL. Una canalización contiene información como qué combinación, topología y atributos usar. Una canalización es inmutable. Si quieres cambiar algunos parámetros de configuración, debes crear otra canalización. WebGPU también usa codificadores de comandos para agrupar comandos y ejecutarlos en el orden en que se registraron. Esto es útil en la asignación de sombras, por ejemplo, en la que, en un solo pase por los objetos, la aplicación puede grabar varias transmisiones de comandos, una para cada mapa de sombras de la luz.
En resumen, como el modelo de estado global de WebGL hacía que la creación de bibliotecas y aplicaciones sólidas y componibles fuera difícil y frágil, WebGPU redujo significativamente la cantidad de estado de la que los desarrolladores debían hacer un seguimiento mientras enviaban comandos a la GPU.
No más sincronización
En las GPU, por lo general, no es eficiente enviar comandos y esperarlos de forma síncrona, ya que esto puede vaciar la canalización y generar burbujas. Esto es especialmente cierto en WebGPU y WebGL, que usan una arquitectura de procesos múltiples con el controlador de GPU que se ejecuta en un proceso independiente de JavaScript.
En WebGL, por ejemplo, llamar a gl.getError()
requiere un IPC síncrono del proceso de JavaScript al proceso de la GPU y viceversa. Esto puede causar una burbuja en el lado de la CPU a medida que se comunican los dos procesos.
A fin de evitar estas burbujas, WebGPU está diseñada para ser completamente asíncrona. El modelo de errores y todas las demás operaciones se realizan de forma asíncrona. Por ejemplo, cuando creas una textura, la operación parece tener éxito de inmediato, incluso si la textura es en realidad un error. Solo puedes descubrir el error de forma asíncrona. Este diseño mantiene la comunicación entre procesos sin burbujas y les brinda a las aplicaciones un rendimiento confiable.
ComputeShader
Los sombreadores de cómputos son programas que se ejecutan en la GPU para realizar cálculos de uso general. Solo están disponibles en WebGPU, no en WebGL.
A diferencia de los sombreadores de vértices y fragmentos, no se limitan al procesamiento de gráficos y se pueden usar para una amplia variedad de tareas, como el aprendizaje automático, la simulación de física y la computación científica. Los sombreadores de cómputos se ejecutan en paralelo por cientos o incluso miles de subprocesos, lo que los hace muy eficientes para procesar grandes conjuntos de datos. Obtén información sobre el procesamiento de GPU y más detalles en este artículo extenso sobre WebGPU.
Procesamiento de fotogramas de video
Procesar fotogramas de video con JavaScript y WebAssembly tiene algunas desventajas: el costo de copiar los datos de la memoria de la GPU a la memoria de la CPU, y el paralelismo limitado que se puede lograr con trabajadores y subprocesos de CPU. WebGPU no tiene esas limitaciones, por lo que es ideal para procesar fotogramas de video gracias a su integración estrecha con la API de WebCodecs.
En el siguiente fragmento de código, se muestra cómo importar un VideoFrame como una textura externa en WebGPU y procesarlo. Puedes probar esta demostración.
// Init WebGPU device and pipeline...
// Configure canvas context...
// Feed camera stream to video...
(function render() {
const videoFrame = new VideoFrame(video);
applyFilter(videoFrame);
requestAnimationFrame(render);
})();
function applyFilter(videoFrame) {
const texture = device.importExternalTexture({ source: videoFrame });
const bindgroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [{ binding: 0, resource: texture }],
});
// Finally, submit commands to GPU
}
Portabilidad de la aplicación de forma predeterminada
WebGPU te obliga a solicitar limits
. De forma predeterminada, requestDevice()
muestra un GPUDevice que puede no coincidir con las capacidades de hardware del dispositivo físico, sino con un denominador común razonable y más bajo de todas las GPUs. Al exigir que los desarrolladores soliciten límites de dispositivos, WebGPU garantiza que las aplicaciones se ejecuten en la mayor cantidad posible de dispositivos.
Manejo de lienzos
WebGL administra automáticamente el lienzo después de que creas un contexto de WebGL y proporcionas atributos de contexto, como alpha, antialias, colorSpace, depth, preserveDrawingBuffer o stencil.
Por otro lado, WebGPU requiere que administres el lienzo por tu cuenta. Por ejemplo, para lograr el suavizado en WebGPU, crearías una textura de múltiples muestras y la renderizarías. Luego, resolverías la textura de múltiples muestras en una textura normal y la dibujarías en el lienzo. Esta administración manual te permite generar tantos lienzos como desees desde un solo objeto GPUDevice. En cambio, WebGL solo puede crear un contexto por lienzo.
Consulta la demostración de varios lienzos de WebGPU.
Como nota al margen, los navegadores actualmente tienen un límite en la cantidad de lienzos WebGL por página. Al momento de escribir este documento, Chrome y Safari solo pueden usar hasta 16 lienzos de WebGL a la vez; Firefox puede crear hasta 200. Por otro lado, no hay límite para la cantidad de lienzos de WebGPU por página.
Mensajes de error útiles
WebGPU proporciona una pila de llamadas para cada mensaje que se muestra desde la API. Esto significa que puedes ver rápidamente dónde se produjo el error en tu código, lo que es útil para depurar y corregir errores.
Además de proporcionar una pila de llamadas, los mensajes de error de WebGPU también son fáciles de entender y realizar acciones en ellos. Por lo general, los mensajes de error incluyen una descripción del error y sugerencias para corregirlo.
WebGPU también te permite proporcionar un label
personalizado para cada objeto WebGPU. Luego, el navegador usa esta etiqueta en los mensajes de GPUError, las advertencias de la consola y las herramientas para desarrolladores del navegador.
De nombres a índices
En WebGL, muchos elementos están conectados por nombres. Por ejemplo, puedes declarar una variable uniforme llamada myUniform
en GLSL y obtener su ubicación con gl.getUniformLocation(program, 'myUniform')
. Esto es útil porque se muestra un error si escribes mal el nombre de la variable uniforme.
Por otro lado, en la WebGPU, todo está completamente conectado por desplazamiento de bytes o índice (lo que a menudo se denomina ubicación). Es tu responsabilidad mantener sincronizadas las ubicaciones del código en WGSL y JavaScript.
Generación de mipmaps
En WebGL, puedes crear el MIP de nivel 0 de una textura y, luego, llamar a gl.generateMipmap()
. Luego, WebGL generará todos los demás niveles de MIP por ti.
En WebGPU, debes generar los mapas de MIP por tu cuenta. No hay una función integrada para hacerlo. Consulta el análisis de las especificaciones para obtener más información sobre la decisión. Puedes usar bibliotecas prácticas, como webgpu-utils, para generar mapas de MIP o aprender a hacerlo por tu cuenta.
Búferes y texturas de almacenamiento
Los búferes uniformes son compatibles con WebGL y WebGPU y te permiten pasar parámetros constantes de tamaño limitado a los sombreadores. Los búferes de almacenamiento, que son muy parecidos a los uniformes, solo son compatibles con WebGPU y son más potentes y flexibles que los uniformes.
Los datos de los búferes de almacenamiento que se pasan a los sombreadores pueden ser mucho más grandes que los búferes uniformes. Si bien la especificación indica que las vinculaciones de búferes uniformes pueden tener un tamaño de hasta 64 KB (consulta
maxUniformBufferBindingSize
), el tamaño máximo de una vinculación de búfer de almacenamiento es de al menos 128 MB en WebGPU (consultamaxStorageBufferBindingSize
).Los búferes de almacenamiento son de escritura y admiten algunas operaciones atómicas, mientras que los búferes uniformes son de solo lectura. Esto permite implementar nuevas clases de algoritmos.
Las vinculaciones de búferes de almacenamiento admiten arrays de tamaño de entorno de ejecución para algoritmos más flexibles, mientras que el sombreador debe proporcionar tamaños de array de búferes uniformes.
Las texturas de almacenamiento solo son compatibles con WebGPU y son para las texturas lo que los búferes de almacenamiento son para los búferes uniformes. Son más flexibles que las texturas regulares y admiten escrituras de acceso aleatorio (y también lecturas en el futuro).
Cambios en el búfer y la textura
En WebGL, puedes crear un búfer o una textura y, luego, cambiar su tamaño en cualquier momento con gl.bufferData()
y gl.texImage2D()
, respectivamente.
En WebGPU, los búferes y las texturas son inmutables. Esto significa que no puedes cambiar su tamaño, uso ni formato después de crearlos. Solo puedes cambiar su contenido.
Diferencias en las convenciones espaciales
En WebGL, el rango de clips de Z es de -1 a 1. En WebGPU, el rango del espacio de recorte en Z es de 0 a 1. Esto significa que los objetos con valor z igual a 0 son los más cercanos a la cámara, mientras que los objetos con valor z igual a 1 son los que están más alejados.
WebGL usa la convención de OpenGL, en la que el eje Y está hacia arriba y el eje Z está hacia el observador. WebGPU usa la convención de Metal, en la que el eje Y está hacia abajo y el eje Z está fuera de la pantalla. Ten en cuenta que la dirección del eje Y es hacia abajo en las coordenadas del búfer de trama, de la vista y del fragmento o píxel. En el espacio de clip, la dirección del eje Y sigue hacia arriba, como en WebGL.
Agradecimientos
Gracias a Corentin Wallez, Gregg Tavares, Stephen White, Ken Russell y Rachel Andrew por revisar este artículo.
También te recomiendo WebGPUFundamentals.org para obtener un análisis detallado de las diferencias entre WebGPU y WebGL.