Novidades da WebGPU (Chrome{/121)

François Beaufort
François Beaufort

Suporte para WebGPU no Android

A equipe do Chrome tem o prazer de anunciar que a WebGPU agora está ativada por padrão no Chrome 121 em dispositivos com o Android 12 e versões mais recentes com GPUs Qualcomm e ARM.

O suporte será expandido gradualmente para abranger uma variedade maior de dispositivos Android, incluindo aqueles com Android 11 em um futuro próximo. Essa expansão vai depender de mais testes e otimização para garantir uma experiência perfeita em uma gama mais ampla de configurações de hardware. Consulte o problema chromium:1497815.

Captura de tela do exemplo da WebGPU em execução no Chrome para Android.
Exemplo da WebGPU em execução no Chrome para Android.

Usar DXC em vez de FXC para compilação de sombreador no Windows

O Chrome agora usa o poder do DXC (DirectX Compiler) para compilar sombreadores em máquinas Windows D3D12 equipadas com hardware gráfico SM6+. Antes, a WebGPU dependia do FXC (FX Compiler) para a compilação de sombreadores no Windows. Embora funcional, o FXC não tinha o conjunto de recursos e as otimizações de desempenho presentes no DXC.

Os testes iniciais mostram um aumento médio de 20% na velocidade de compilação do sombreador de computação ao usar o DXC em comparação com o FXC.

Consultas de carimbo de data/hora em passagens de computação e renderização

As consultas de carimbo de data/hora permitem que os aplicativos da WebGPU meçam com precisão (até nanossegundos) quanto tempo os comandos da GPU levam para executar os passes de computação e renderização. Eles são muito usados para conseguir insights sobre o desempenho e o comportamento das cargas de trabalho da GPU.

Quando o recurso "timestamp-query" estiver disponível em um GPUAdapter, você poderá fazer o seguinte:

  • Solicite um GPUDevice com o recurso "timestamp-query".
  • Crie um GPUQuerySet do tipo "timestamp".
  • Use GPUComputePassDescriptor.timestampWrites e GPURenderPassDescriptor.timestampWrites para definir onde gravar os valores de carimbo de data/hora em GPUQuerySet.
  • Resolva os valores do carimbo de data/hora em uma GPUBuffer com resolveQuerySet().
  • Leia os valores do carimbo de data/hora copiando os resultados do GPUBuffer para a CPU.
  • Decodifique os valores de carimbo de data/hora como um BigInt64Array.

Confira o exemplo a seguir sobre o problema dawn:1800.

const adapter = await navigator.gpu.requestAdapter();
if (!adapter.features.has("timestamp-query")) {
  throw new Error("Timestamp query feature is not available");
}
// Explicitly request timestamp query feature.
const device = await adapter.requestDevice({
  requiredFeatures: ["timestamp-query"],
});
const commandEncoder = device.createCommandEncoder();

// Create a GPUQuerySet which holds 2 timestamp query results: one for the
// beginning and one for the end of compute pass execution.
const querySet = device.createQuerySet({ type: "timestamp", count: 2 });
const timestampWrites = {
  querySet,
  beginningOfPassWriteIndex: 0, // Write timestamp in index 0 when pass begins.
  endOfPassWriteIndex: 1, // Write timestamp in index 1 when pass ends.
};
const passEncoder = commandEncoder.beginComputePass({ timestampWrites });
// TODO: Set pipeline, bind group, and dispatch work to be performed.
passEncoder.end();

// Resolve timestamps in nanoseconds as a 64-bit unsigned integer into a GPUBuffer.
const size = 2 * BigInt64Array.BYTES_PER_ELEMENT;
const resolveBuffer = device.createBuffer({
  size,
  usage: GPUBufferUsage.QUERY_RESOLVE | GPUBufferUsage.COPY_SRC,
});
commandEncoder.resolveQuerySet(querySet, 0, 2, resolveBuffer, 0);

// Read GPUBuffer memory.
const resultBuffer = device.createBuffer({
  size,
  usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
});
commandEncoder.copyBufferToBuffer(resolveBuffer, 0, resultBuffer, 0, size);

// Submit commands to the GPU.
device.queue.submit([commandEncoder.finish()]);

// Log compute pass duration in nanoseconds.
await resultBuffer.mapAsync(GPUMapMode.READ);
const times = new BigInt64Array(resultBuffer.getMappedRange());
console.log(`Compute pass duration: ${Number(times[1] - times[0])}ns`);
resultBuffer.unmap();

Devido a preocupações com ataques de tempo, as consultas de carimbo de data/hora são quantizadas com uma resolução de 100 microssegundos, o que proporciona um bom equilíbrio entre precisão e segurança. No navegador Chrome, é possível desativar a quantização de carimbo de data/hora ativando a sinalização "Recursos do desenvolvedor da WebGPU" em chrome://flags/#enable-webgpu-developer-features durante o desenvolvimento do app. Consulte Quantização de consultas de carimbo de data/hora para saber mais.

Como as GPUs podem redefinir o contador de carimbo de data/hora de vez em quando, o que pode resultar em valores inesperados, como deltas negativos entre os carimbos de data/hora, recomendamos que você confira as alterações de diferenças do Git (em inglês) que adicionam suporte à consulta de carimbo de data/hora no seguinte exemplo de Compute Boids.

Captura de tela da amostra do Compute Boids com consulta de carimbo de data/hora.
Amostra do Compute Boids com consulta de carimbo de data/hora.

Pontos de entrada padrão para módulos de sombreador

Para melhorar a experiência do desenvolvedor, agora é possível omitir o entryPoint do módulo de sombreador ao criar um pipeline de computação ou de renderização. Se nenhum ponto de entrada exclusivo para o estágio do sombreador for encontrado no código do sombreador, um GPUValidationError será acionado. Confira o exemplo a seguir e o issue dawn:2254.

const code = `
    @vertex fn vertexMain(@builtin(vertex_index) i : u32) ->
      @builtin(position) vec4f {
       const pos = array(vec2f(0, 1), vec2f(-1, -1), vec2f(1, -1));
       return vec4f(pos[i], 0, 1);
    }
    @fragment fn fragmentMain() -> @location(0) vec4f {
      return vec4f(1, 0, 0, 1);
    }`;
const module = myDevice.createShaderModule({ code });
const format = navigator.gpu.getPreferredCanvasFormat();
const pipeline = await myDevice.createRenderPipelineAsync({
  layout: "auto",
  vertex: { module, entryPoint: "vertexMain" },
  fragment: { module, entryPoint: "fragmentMain", targets: [{ format }] },
  vertex: { module },
  fragment: { module, targets: [{ format }] },
});

Suporte a display-p3 como espaço de cores GPUExternalTexture

Agora você pode definir um espaço de cor de destino "display-p3" ao importar uma GPUExternalTexture de vídeos HDR com importExternalTexture(). Veja como a WebGPU lida com espaços de cor. Confira o exemplo a seguir e informe o problema chromium:1330250.

// Create texture from HDR video.
const video = document.querySelector("video");
const texture = myDevice.importExternalTexture({
  source: video,
  colorSpace: "display-p3",
});

Informações de heaps de memória

Para ajudar a prever as limitações de memória ao alocar grandes quantidades de dados durante o desenvolvimento do app, o requestAdapterInfo() agora expõe informações de memoryHeaps, como tamanho e tipo de heap de memória disponíveis no adaptador. Esse recurso experimental só pode ser acessado quando a flag "Recursos do desenvolvedor da WebGPU" em chrome://flags/#enable-webgpu-developer-features estiver ativada. Confira o exemplo a seguir e o issue dawn:2249.

const adapter = await navigator.gpu.requestAdapter();
const adapterInfo = await adapter.requestAdapterInfo();

for (const { size, properties } of adapterInfo.memoryHeaps) {
  console.log(size); // memory heap size in bytes
  if (properties & GPUHeapProperty.DEVICE_LOCAL)  { /* ... */ }
  if (properties & GPUHeapProperty.HOST_VISIBLE)  { /* ... */ }
  if (properties & GPUHeapProperty.HOST_COHERENT) { /* ... */ }
  if (properties & GPUHeapProperty.HOST_UNCACHED) { /* ... */ }
  if (properties & GPUHeapProperty.HOST_CACHED)   { /* ... */ }
}
Captura de tela de https://webgpureport.org mostrando heaps de memória nas informações do adaptador.
Heaps de memória de informações do adaptador mostrados em https://webgpureport.org (link em inglês).

Atualizações ao amanhecer

Os métodos HasWGSLLanguageFeature e EnumerateWGSLLanguageFeatures em wgpu::Instance foram adicionados para processar recursos de linguagem da WGSL. Consulte o problema dawn:2260.

O recurso wgpu::Feature::BufferMapExtendedUsages não padrão permite criar um buffer de GPU com wgpu::BufferUsage::MapRead ou wgpu::BufferUsage::MapWrite e qualquer outro wgpu::BufferUsage. Confira o exemplo a seguir e informe o problema dawn:2204.

wgpu::BufferDescriptor descriptor = {
  .size = 128,
  .usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::Uniform
};
wgpu::Buffer uniformBuffer = device.CreateBuffer(&descriptor);

uniformBuffer.MapAsync(wgpu::MapMode::Write, 0, 128,
   [](WGPUBufferMapAsyncStatus status, void* userdata)
   {
      wgpu::Buffer* buffer = static_cast<wgpu::Buffer*>(userdata);
      memcpy(buffer->GetMappedRange(), data, sizeof(data));
   },
   &uniformBuffer);

Os seguintes recursos foram documentados: Compartilhamento de textura ANGLE, Proteção múltipla D3D11, Sincronização de dispositivo implícita, Formatos de textura Norm16, Consulta de carimbo de data/hora dentro de cartões, Armazenamento local do Pixel, Recursos do sombreador e Formatos multiplanares.

A equipe do Chrome criou um repositório oficial no GitHub para a Dawn (em inglês).

Esses são apenas alguns dos principais destaques. Confira a lista completa de confirmações (link em inglês).

Novidades da WebGPU

Uma lista de tudo o que foi abordado na série O que há de novo na WebGPU.

Chrome 125

Chrome 124

Chrome 123

Chrome 122

Chrome 121

Google Chrome 120

Chrome 119

Chrome 118

Chrome 117

Chrome 116

Chrome 115

Chrome 114

Google Chrome 113