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 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 que serão executados no 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 variedade maior de configurações de hardware. Consulte issue chromium:1497815.

Captura de tela da amostra da WebGPU em execução no Chrome para Android.
Exemplo de 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 a potência do DXC (DirectX Compiler) para compilar sombreadores em máquinas Windows D3D12 equipadas com o 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é nanosegundos) quanto tempo os comandos da GPU levam para executar passagens de computação e renderização. Elas são muito usadas para gerar 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 valores de carimbo de data/hora em GPUQuerySet.
  • Resolva valores de carimbo de data/hora em uma GPUBuffer com resolveQuerySet().
  • Leia os valores de carimbo de data/hora copiando os resultados de GPUBuffer para a CPU.
  • Decodifique os valores de carimbo de data/hora como um BigInt64Array.

Confira o exemplo abaixo e informe 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 ataque de tempo, as consultas de carimbo de data/hora são quantificadas com uma resolução de 100 microssegundos, o que oferece um bom meio-termo entre precisão e segurança. No navegador Chrome, é possível desativar a quantização de carimbo de data/hora ativando os "Recursos para desenvolvedores WebGPU". flag 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 carimbos de data/hora, recomendo que você confira as alterações de diferenças do Git que adicionam suporte à consulta de carimbo de data/hora na amostra do Compute Boids a seguir.

Captura de tela da amostra do Compute Boids com a consulta de carimbo de data/hora.
Amostra do Compute Boids com uma 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 do estágio do sombreador for encontrado no código dele, um GPUValidationError será acionado. Confira o exemplo a seguir e o problema dawn:2254 (link em inglês).

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 o espaço de cor de destino do "display-p3" ao importar uma GPUExternalTexture de vídeos em HDR com importExternalTexture(). Confira como a WebGPU lida com espaços de cor. Confira o exemplo a seguir e veja 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 limitações de memória ao alocar grandes quantidades durante o desenvolvimento do app, o requestAdapterInfo() agora expõe informações do memoryHeaps, como tamanho e tipo de heaps de memória disponíveis no adaptador. Esse recurso experimental só pode ser acessado quando os "Recursos para desenvolvedores WebGPU" flag em chrome://flags/#enable-webgpu-developer-features está ativada. Confira o exemplo a seguir e o problema dawn:2249 (link em inglês).

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 com informações do adaptador.
Heaps de memória de informações do adaptador mostrados em https://webgpureport.org (link em inglês).

Atualizações do amanhecer

Os métodos HasWGSLLanguageFeature e EnumerateWGSLLanguageFeatures em wgpu::Instance foram adicionados para processar recursos de idioma 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 abaixo 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, Recursos do sombreador D3D11, Sincronização implícita de dispositivo, formatos de textura Norm16, Marcação de tempo de consulta dentro de cartões, Armazenamento local do Pixel, Recursos do sombreador e Formatos multiplano.

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

Isso cobre 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 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