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 Android 12 e versões mais recentes e GPUs Qualcomm e ARM.

O suporte será ampliado gradualmente para abranger uma variedade maior de dispositivos Android, incluindo aqueles que executam o 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 o problema chromium:1497815.

Captura de tela do exemplo 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 o poder do DXC (DirectX Compiler) para compilar shaders em máquinas Windows D3D12 equipadas com hardware gráfico SM6+. Antes, a WebGPU dependia do FXC (compilador FX) para a compilação de shaders 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 de sombreadores 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 da WebGPU meçam com precisão (até o nanossegundo) quanto tempo os comandos da GPU levam para executar os passes de renderização e computação. Eles são usados principalmente para ter insights sobre o desempenho 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 uma GPUQuerySet do tipo "timestamp".
  • Use GPUComputePassDescriptor.timestampWrites e GPURenderPassDescriptor.timestampWrites para definir onde gravar valores de carimbo de data/hora em GPUQuerySet.
  • Resolva os valores de carimbo de data/hora em um GPUBuffer com resolveQuerySet().
  • Leia os valores de carimbo de data/hora copiando os resultados do GPUBuffer para a CPU.
  • Decodificar 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 questões de 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, você pode desativar a quantização de carimbos de data/hora ativando a flag "WebGPU Developer Features" em chrome://flags/#enable-webgpu-developer-features durante o desenvolvimento do app. Consulte Quantização de consultas de carimbos 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 do exemplo do Compute Boids com a consulta de carimbo de data/hora.
Exemplo de Compute Boids com a 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 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 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 é 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 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 sobre pilhas 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 memoryHeaps, como o tamanho e o tipo de pilhas de memória disponíveis no adaptador. Esse recurso experimental só pode ser acessado quando a flag "WebGPU Developer Features" em chrome://flags/#enable-webgpu-developer-features está ativada. Confira o exemplo a seguir e 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 com pilhas de memória nas informações do adaptador.
Montões de memória de informações do adaptador mostrados em https://webgpureport.org.

Atualizações do Dawn

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 e o problema dawn:2204 a seguir.

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 do ANGLE, proteção de vários segmentos do D3D11, sincronização implícita de dispositivos, formatos de textura Norm16, consulta de carimbo de data/hora dentro de transmissões, armazenamento local de pixels, recursos de sombreador 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 confirmações.

Novidades na WebGPU

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

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