Co nowego w WebGPU (Chrome 121)

François Beaufort
François Beaufort

Obsługa WebGPU na urządzeniach z Androidem

Zespół Chrome z przyjemnością informuje, że protokół WebGPU jest teraz domyślnie włączony w Chrome 121 na urządzeniach z Androidem 12 lub nowszym z procesorami graficznymi Qualcomm i ARM.

Obsługa będzie stopniowo rozszerzana na szerszą gamę urządzeń z Androidem, w tym te z Androidem 11, które w najbliższej przyszłości będą miały system. Rozszerzenie to będzie uzależnione od dalszych testów i optymalizacji, aby zapewnić płynną obsługę na większej liczbie konfiguracji sprzętowych. Zobacz issue chromium:1497815.

Zrzut ekranu z przykładowym interfejsem WebGPU uruchomiony w Chrome na Androida.
Przykładowy interfejs WebGPU działający w Chrome na Androida.

Do kompilacji programu do cieniowania w systemie Windows użyj DXC zamiast FXC

Chrome korzysta teraz z kompilacji DXC (DirectX Compiler) do kompilowania programów do cieniowania na komputerach z systemem Windows D3D12 i sprzętem graficznym SM6+. Wcześniej do kompilacji programu do cieniowania w systemie Windows używany był komponent FXC (FX Compiler). Chociaż w FXC brakowało zbioru funkcji i optymalizacji wydajności dostępnych w DXC,

Wstępne testy pokazują średnio 20-procentowy wzrost szybkości kompilacji w cieniowaniu obliczeniowym w przypadku korzystania z technologii DXC w porównaniu z trybem FXC.

Zapytania o sygnatury czasowe podczas obliczeń i renderowania

Zapytania o sygnaturę czasową pozwalają aplikacjom WebGPU na precyzyjne pomiary (z dokładnością do nanosekundy) czasu potrzebnego przez polecenia GPU na wykonanie obliczeń i przebiegów renderowania. Są one często używane do uzyskiwania statystyk dotyczących wydajności i zachowania zadań GPU.

Gdy funkcja "timestamp-query" zostanie udostępniona w GPUAdapter, będzie można teraz wykonywać te czynności:

  • Poproś o GPUDevice z funkcją "timestamp-query".
  • Utwórz GPUQuerySet typu "timestamp".
  • Użyj właściwości GPUComputePassDescriptor.timestampWrites i GPURenderPassDescriptor.timestampWrites, aby określić, gdzie w polu GPUQuerySet mają być zapisywane wartości sygnatury czasowej.
  • Zapisz wartości sygnatur czasowych w elemencie GPUBuffer za pomocą polecenia resolveQuerySet().
  • Odczytaj wartości sygnatur czasowych, kopiując wyniki z GPUBuffer do CPU.
  • Dekoduj wartości sygnatury czasowej jako BigInt64Array.

Zobacz ten przykład problemu: 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();

Ze względu na obawy o atak czasowy zapytania o sygnaturę czasową są kwantyzowane z rozdzielczością 100 mikrosekund, co zapewnia dobry kompromis między precyzją a bezpieczeństwem. Kwantyzację sygnatury czasowej możesz wyłączyć w przeglądarce Chrome, włączając flagę „Funkcje dla programistów WebGPU” na poziomie chrome://flags/#enable-webgpu-developer-features podczas tworzenia aplikacji. Więcej informacji znajdziesz w artykule na temat kwantyzacji zapytań o sygnaturę czasową.

Procesory graficzne mogą od czasu do czasu resetować licznik sygnatur czasowych, co może skutkować nieoczekiwanymi wartościami, takimi jak ujemne różnice między sygnaturami czasowymi, dlatego zalecamy zapoznanie się z narzędziem git diff changes, które dodaje obsługę zapytań o sygnaturę czasową do poniższego przykładu Compute Boids.

Zrzut ekranu z przykładem Compute Boid z zapytaniem o sygnaturę czasową.
Przykładowy obiekt Compute Boids zawierający zapytanie o sygnaturę czasową.

Domyślne punkty wejścia do modułów cieniowania

Aby usprawnić działanie programistów, możesz teraz pomijać entryPoint modułu cieniowania podczas tworzenia potoku obliczeniowego lub renderowania. Jeśli w kodzie programu do cieniowania nie znaleziono unikalnego punktu wejścia dla etapu cieniowania, wywoływany jest błąd GPUValidationError. Zobacz ten przykład i ten problem: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 }] },
});

Obsługuj parametr display-p3 jako przestrzeń kolorów GPUExternalTexture

Teraz możesz ustawić docelową przestrzeń kolorów "display-p3" podczas importowania elementu GPUExternalTexture z filmów HDR za pomocą importExternalTexture(). Zobacz, jak WebGPU obsługuje przestrzenie kolorów. Zobacz ten przykład problemu chromium:1330250.

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

Informacje o stertach pamięci

Aby pomóc w przewidywaniu ograniczeń pamięci podczas przydzielania dużych ilości pamięci podczas tworzenia aplikacji, requestAdapterInfo() udostępnia teraz informacje o memoryHeaps, takie jak rozmiar i typ stosu pamięci dostępnych w adapterze. Ta funkcja eksperymentalna jest dostępna tylko wtedy, gdy włączona jest flaga „Funkcje dla programistów WebGPU” na stronie chrome://flags/#enable-webgpu-developer-features. Zobacz ten przykład i ten problem: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)   { /* ... */ }
}
Zrzut ekranu ze strony https://webgpureport.org z widocznymi stertami pamięci w informacjach o adapterze.
Stosy pamięci z informacjami o adapterze wyświetlane na stronie https://webgpureport.org.

Powiadomienia o świcie

Metody HasWGSLLanguageFeature i EnumerateWGSLLanguageFeatures w wgpu::Instance zostały dodane, by umożliwić obsługę funkcji języka WGSL. Zobacz problem: dawn:2260.

Niestandardowa funkcja wgpu::Feature::BufferMapExtendedUsages umożliwia utworzenie bufora GPU za pomocą wgpu::BufferUsage::MapRead lub wgpu::BufferUsage::MapWrite oraz wszystkich innych elementów wgpu::BufferUsage. Zobacz ten przykład problemu: 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);

Opracowaliśmy dokumentację tych funkcji: ANGLE TextureSharing (Udostępnianie tekstury ANGLE), ochrona wielowątkowa D3D11, intuicyjna synchronizacja urządzenia, formaty tekstur Norm16, Timestamp Query Inside Pass, Lokalna pamięć masowa Pixela, funkcje tasowania i formaty wielopoziomowe.

Zespół Chrome utworzył oficjalne repozytorium na GitHubie dla Dawn.

To tylko niektóre z najważniejszych kwestii. Zobacz pełną listę zatwierdzeń.

Co nowego w WebGPU

Lista wszystkich zagadnień omówionych w serii Co nowego w WebGPU.

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