Novidades da WebGPU (Chrome{/121)

François Beaufort
François Beaufort

Suporte à WebGPU no Android

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

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

Captura de tela do exemplo 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 shader no Windows

Agora, o Chrome usa o poder do DXC (compilador DirectX) para compilar shaders em máquinas Windows D3D12 equipadas com hardware gráfico SM6+. Antes, o WebGPU dependia do FXC (FX Compiler) para a compilação de shader 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 shader de computação ao usar o DXC em comparação com o FXC.

Consultas de carimbo de data/hora em transmissões de computação e renderização

As consultas de carimbo de data/hora permitem que os aplicativos WebGPU meçam com precisão (até o nanossegundo) quanto tempo os comandos da GPU levam para executar computação e renderização. Elas são muito usadas para gerar insights sobre a performance e o comportamento das cargas de trabalho de GPU.

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

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

Confira o exemplo a seguir e execute 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 oferece um bom compromisso entre precisão e segurança. No navegador Chrome, é possível desativar a quantização de carimbos de data/hora ativando a flag "Recursos para desenvolvedores do 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 carimbos de data/hora ocasionalmente, o que pode resultar em valores inesperados, como deltas negativos entre carimbos de data/hora, recomendo que você confira as mudanças do git diff que adicionam suporte a consultas de carimbos de data/hora ao exemplo Compute Boids a seguir.

Captura de tela do exemplo Compute Boids com consulta de carimbo de data/hora.
Exemplo do Compute Boids com consulta de carimbo de data/hora.

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

Para melhorar a experiência do desenvolvedor, agora é possível omitir o entryPoint do módulo de shader ao criar um pipeline de computação ou renderização. Se nenhum ponto de entrada exclusivo para a etapa do shader for encontrado no código, um GPUValidationError será acionado. Confira o exemplo a seguir e 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 }] },
});

Adicionar suporte a display-p3 como espaço de cor GPUExternalTexture

Agora é possível definir o espaço de cores de destino "display-p3" ao importar uma GPUExternalTexture de vídeos HDR com importExternalTexture(). Confira como a WebGPU lida com espaços de cores. Confira o exemplo a seguir e 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 sobre heaps de memória

Para ajudar você a prever limitações de memória ao alocar grandes quantidades durante o desenvolvimento do app, o requestAdapterInfo() agora expõe informações de memoryHeaps, como o tamanho e o tipo de heaps de memória disponíveis no adaptador. Esse recurso experimental só fica acessível quando a flag "Recursos para desenvolvedores do WebGPU" em chrome://flags/#enable-webgpu-developer-features está ativada. Confira o exemplo a seguir e o problema 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 com informações do adaptador mostrados em https://webgpureport.org.

Atualizações do amanhecer

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

O recurso não padrão wgpu::Feature::BufferMapExtendedUsages 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 emita 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, D3D11 multithread protected, Sincronização implícita de dispositivos, Formatos de textura Norm16, Consulta de carimbo de data/hora em transmissões, Armazenamento local de pixels, Recursos de shader e Formatos multiplanares.

A equipe do Chrome criou um repositório oficial do GitHub para o Dawn.

Isso abrange apenas alguns dos principais destaques. Confira a lista completa de commits.

Novidades no WebGPU

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

Chrome 140

Chrome 139

Chrome 138

Chrome 137

Chrome 136

Chrome 135

Chrome 134

Chrome 133

Chrome 132

Chrome 131

Chrome 130

Chrome 129

Chrome 128

Chrome 127

Chrome 126

Chrome 125

Chrome 124

Chrome 123

Chrome 122

Chrome 121

Chrome 120

Chrome 119

Chrome 118

Chrome 117

Chrome 116

Chrome 115

Chrome 114

Chrome 113