Webgpu и цена на компиляцию WGSL – Hugo Daniel

Представление информации и инструмента времени WebGPU

Добро пожаловать, в двух предыдущих постах я пошел на поиски пределов WGSL (оба
в спецификацииа также
из этого)

В этом посте я хочу представить простое приложение WebGPU, где вы можете создать шейдеры WGSL, перетаскивая несколько ползунков, чтобы увеличить их сложность.

Идея состоит в том, что время компиляции и сложность могут быть известны для вычислительных и рендеринговых трубопроводов, а также на разных этапах (вершина и фрагмент).

Это важно для меня, потому что я хотел знать, сколько стоит увеличить сложность шейдера и где можно начать, чтобы начать использовать буферы/текстуры для некоторых из них.

Сколько времени нужно, чтобы собрать 3MEGABYTE Vertex Shader?

Легко узнать, вот единственный результат холодных времен в Chrome (140.0.7339.215) от My Intel MacBook Pro (2019):

Как насчет 30 -мегабайтного вершин -шейдера ??

Ошибка компиляции вершин -шейдера

Это ошибки. Ах!

Логотип WebGPU с секундомом

Простой инструмент, в духе потрясающего
Отчет WebGPUно не так исчерпывающе.

Вы можете найти его здесь:
Инструмент диагностики WebGPU (и эталона компиляции)Полем

Мое намерение состояло в том, чтобы составить его из 3 основных частей:

  1. Простой образец (вращающийся треугольник).
  2. Сложность шейдера ползунков и времен (компиляция, запрос на адаптер и создание трубопровода).
  3. Информация о устройствах и адаптере (функции и объявленные ограничения)

Вращающийся треугольник

Вращающийся треугольник сделан из 3 начальных шейдеров. Компьютный шейдер выводит вершины, затем вершино -шейдер преобразует их, и, наконец, фрагментный шейдер рисует его.

Я считаю это своего рода «Hello World» для WebGPU, но также и о простой программе, которая выполняет счастливый путь самого быстрого рендеринга (или близко к нему).

Идея вдоль приложения заключается в том, что этот треугольник никогда не меняетсяПолем Это всегда остается таким же, как и сложность шейдера.

Сложность Шейдера Слайдеры

Сложность шейдера может быть увеличена на каждой стадии трубопровода. Это включает в себя отделение вершин -шейдера от фрагментного шейдера на разные модули, чтобы позволить проверить, насколько они отличаются (если они вообще делают).

Изображение ползунков шейдеров в инструменте диагностики WebGPU

Строка шейдера создается до начала компиляции. Это делается в потоке веб -работников, чтобы поддерживать реагирование (как можно больше) по мере того, как увеличивается время прохождения памяти и строки.

Read more:  Закрытие фондового рынка: Sensex продлевает выигрышную серию до третьего дня, увеличившись на 208 пунктов; Nifty заканчивается на отметке 25 935 | Новости рынков

Я хотел, чтобы эти ползунки были смещены в сторону сложности. Небольшое изменение в них должно немного увеличить размер шейдера. Сделать их целенаправленно неуравновешенными, чтобы гарантировать, что пределы устройств могут быть проверены.

Какой код WGSL генерируют ползунки?

Они разделены на три категории:

  1. Количество функций – сброс N функций с 1 f32 Аргумент каждый, кто возвращает единственную переменную, объявленную в теле.
  2. Количество операторов – для каждой функции в 1., создает n операторов, которые просто выполняют простое назначение.
  3. Глубина выражения – для каждого утверждения в 2. Расширяет его, чтобы иметь n сумм.

Поэтому, если мы установим их все на 8 (8 функций, каждая с 8 операторами, каждая с 8 суммами), он создает файл 5KILOBYTE.

Вы можете проверить код генератора
здесьПолем

Или нажмите здесь, чтобы увидеть выход, произведенный с тремя ползунками в значении 8

struct Vertex { position: vec3f, color: vec3f, }

struct Params { angle: f32, }

// Storage buffer that will hold our generated vertices @group(0) @binding(0) var vertices: array; @group(0) @binding(1) var params: Params;

fn function0(a: f32) -> f32 { var tmp: f32 = 0.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; return a + tmp; }

fn function1(a: f32) -> f32 { var tmp: f32 = 0.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; return a + tmp; }

fn function2(a: f32) -> f32 { var tmp: f32 = 0.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; return a + tmp; }

fn function3(a: f32) -> f32 { var tmp: f32 = 0.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; return a + tmp; }

fn function4(a: f32) -> f32 { var tmp: f32 = 0.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; return a + tmp; }

fn function5(a: f32) -> f32 { var tmp: f32 = 0.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; return a + tmp; }

fn function6(a: f32) -> f32 { var tmp: f32 = 0.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; return a + tmp; }

fn function7(a: f32) -> f32 { var tmp: f32 = 0.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; tmp += 0.0+1.0+2.0+3.0+4.0+5.0+6.0+7.0; return a + tmp; }

// Compute shader that generates a triangle's vertices @compute @workgroup_size(1) fn compute_main(@builtin(global_invocation_id) global_id: vec3u) { var accum: f32 = 0.0; accum += function0(8.0); accum += function1(8.0); accum += function2(8.0); accum += function3(8.0); accum += function4(8.0); accum += function5(8.0); accum += function6(8.0); accum += function7(8.0);

// Generate 3 vertices for a triangle // We only need one invocation for this simple example if (global_id.x == 0) { let angle = params.angle + accum * 0.0001; let cos_angle = cos(angle); let sin_angle = sin(angle); let base_positions = array( vec2f(0.0, 0.5), vec2f(-0.5, -0.8), vec2f(0.5, -0.5) );

  let top = vec2f(
      base_positions[0].x * cos_angle - base_positions[0].y * sin_angle,
      base_positions[0].x * sin_angle + base_positions[0].y * cos_angle,
  );
  let left = vec2f(
      base_positions[1].x * cos_angle - base_positions[1].y * sin_angle,
      base_positions[1].x * sin_angle + base_positions[1].y * cos_angle,
  );
  let right = vec2f(
      base_positions[2].x * cos_angle - base_positions[2].y * sin_angle,
      base_positions[2].x * sin_angle + base_positions[2].y * cos_angle,
  );

  let depth = clamp(accum * 0.001, -0.5, 0.5);

  // Top vertex (red)
  vertices[0].position = vec3f(top, depth);
  vertices[0].color = vec3f(1.0, 0.0, 0.0);
  
  // Bottom-left vertex (green)
  vertices[1].position = vec3f(left, depth);
  vertices[1].color = vec3f(0.0, 1.0, 0.0);
  
  // Bottom-right vertex (blue)
  vertices[2].position = vec3f(right, depth);
  vertices[2].color = vec3f(0.0, 0.0, 1.0);

}}


Информация о устройстве и адаптере

Особенности и ограничения – это хорошая вещь, чтобы иметь в таких инструментах. Они не используются, просто общаются. Многие из них я не знаю, что они делают, поэтому я включил ссылку на соответствующую главу Spec Spec.

Важной частью здесь является то, что я хотел разделить адаптер ограничения от устройство ограничения. Они могут быть разными, и знание этих различий может быть важным, когда мы стремимся к верхнему концу участка.

В WebGPU пределы адаптера можно рассматривать как объявленные значения того, что может существовать, в то время как ограничения устройства можно рассматривать как то, с чем мы должны работать.

Они могут быть запрошены через
Требуемые limits
на творении.

Еще одна важная часть мотивации для этого – это использовать
скукаПолем Минимальная структура JS, которую я создал как забавный проект, который пытается сделать то, что делают основные рамки JS, но с другим подходом только потому, почему нет? (Нет связывания, нет компиляции, без министрации и небольшого размера).

Было весело и круто видеть, что он работает без комплекта/компиляции при обработке сложности WebGPU.

Заключение

Иди, поиграй с этим немного здесь И дайте мне знать, как ваше устройство и браузер реагируют на массивные шейдеры.

Я обнаружил, что существует значительная разница между холодным и теплым временем компиляции. Это различие с холодным и теплым временем может быть интересно для дальнейшего изучения, и, возможно, его можно даже использовать/эксплуатировать, чтобы скрыть чрезвычайно сложные шейдеры, разделяя их на многих небольших трубопроводах (я думаю, просто дикая гипотеза).

Этот пост завершает усилия WGSL. Для следующих я мог бы представить двигатель рендеринга, который я работаю, который пытается выступить с тем же духом, что и скука для фреймворков JS. Спасибо, что прочитали все это и следите за обновлениями!

2025-10-06 21:14:00


1759789768
#Webgpu #цена #на #компиляцию #WGSL #Hugo #Daniel

Читайте также

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.