wgsl.runWGSL Öğren
Türkçe English

Bölüm 01

Temeller ve Yapı

WGSL sadece shader sözdizimi değildir. Çalışan bir WGSL modülü, browser tarafındaki WebGPU kodu, WebGPU doğrulayıcısı ve GPU pipeline'ı arasında yapılan açık bir sözleşmedir. Bu bölüm o sözleşmeyi gözle okunur hale getirir.

Bu bölümün kernel'ini Studio'da aç.

Aşağıdaki kaynakla aynı projeyi açıp diagnostics, reflection, bind group layout, invocation trace ve compute readback yüzeylerini birlikte kontrol et.

Studio'da aç

Önce modülün tamamını oku

Bir WGSL dosyası bir modüldür. İçinde struct, const, override, module-scope variable, helper function ve entry point bulunabilir. Pratik okuma sırası şu: önce kaynaklar, sonra yardımcı fonksiyonlar, en son entry point.

hello-compute.wgsltam modül
@group(0) @binding(0)
var<storage, read_write> data : array<f32>;

@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) id : vec3u) {
  let i = id.x;
  data[i] = sin(f32(i) * 0.0625);
}
resource

API sınırından hangi veri geçiyor?

Burada data adlı tek storage buffer var. Reflection onu group 0, binding 0 olarak göstermeli.

entry

Pipeline nereden başlıyor?

@compute, main fonksiyonunu compute entry point yapar.

shape

Kaç lane birlikte çalışıyor?

@workgroup_size(64), x boyutunda 64 local invocation'lık workgroup tanımlar.

effect

Her invocation ne yazıyor?

global_invocation_id.x array index olur; lane 0 data[0], lane 1 data[1] yazar.

Attribute süs değildir

WGSL'de attribute'lar sıradan görünen declaration'ları GPU pipeline anlamına bağlar. Aynı fonksiyon gövdesi, önündeki attribute'a göre helper function da olabilir, entry point de.

attribute rollerisoldan sağa oku
@group(0) @binding(0)
var<storage, read_write> data : array<f32>;

@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) id : vec3u) {
  data[id.x] = 1.0;
}

@group ve @binding host tarafındaki slot'u belirtir. @compute shader aşamasını seçer. @workgroup_size yerel paralel şekli sabitler. @builtin ise değerin buffer'dan değil WebGPU tarafından geleceğini söyler.

Studio hareketi: @binding(0) satırını sil ve Diagnostics'e bak. Sonra geri koyup Reflection'da resource'un tekrar göründüğünü kontrol et.

Binding ve address space host sözleşmesidir

Module-scope var declaration'ları shader dışındaki veriyi tarif eder. Address space verinin nerede yaşadığını, access mode ise shader'ın onu nasıl kullanacağını gösterir.

host-visible kaynaklaruniform + storage
struct Params {
  scale : f32,
  offset : f32,
};

@group(0) @binding(0)
var<uniform> params : Params;

@group(0) @binding(1)
var<storage, read_write> data : array<f32>;

uniform küçük, paylaşılan ve read-only ayarlar için iyidir: time, resolution, camera matrix gibi. storage ise büyük array'ler, compute çıktıları, particle state veya readback için daha doğaldır.

Entry point girdileri tahmin edilmez

Entry point yalnızca senin yazdığın parametreleri alır. Compute tarafında ilk öğrenilecek builtin global_invocation_id, fragment tarafında ise position olur.

fragment koordinatıhangi piksel?
struct Frame {
  resolution : vec2f,
  time : f32,
};

@group(0) @binding(0)
var<uniform> frame : Frame;

@fragment
fn fs(@builtin(position) frag : vec4f) -> @location(0) vec4f {
  let uv = frag.xy / frame.resolution;
  return vec4f(uv.x, uv.y, 0.5, 1.0);
}

İlk görsel hamle şu: piksel pozisyonunu çözünürlüğe böl, UV koordinatı elde et, sonra UV'yi renge yaz. Böylece koordinat modelini doğrudan ekranda görürsün.

Dispatch modeli

Compute shader bir kez çalışmaz; çok sayıda invocation olarak planlanır. JavaScript tarafı kaç workgroup dispatch edileceğini seçer, WGSL tarafı da her workgroup içindeki local invocation sayısını söyler.

workgroup_size workgroup başına lane
dispatchWorkgroups workgroup sayısı
global_invocation_id tek lane'in global koordinatı
bounded writeson workgroup güvenliği
@group(0) @binding(0)
var<storage, read_write> data : array<f32>;

@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) id : vec3u) {
  let i = id.x;
  if (i >= arrayLength(&data)) {
    return;
  }

  data[i] = sin(f32(i) * 0.0625);
}

Küçük görsel merdiven

Compute veri akışını öğretir; fragment shader uzamsal sezgiyi öğretir. UV, time, distance field ve smoothstep aynı dersin görsel tarafıdır.

uv-time-sdf.wgslfragment sezgisi
struct Frame {
  resolution : vec2f,
  time : f32,
};

@group(0) @binding(0)
var<uniform> frame : Frame;

@fragment
fn fs(@builtin(position) frag : vec4f) -> @location(0) vec4f {
  let aspect = frame.resolution.x / frame.resolution.y;
  let uv = (frag.xy / frame.resolution - vec2f(0.5, 0.5)) * vec2f(aspect, 1.0);

  let radius = 0.28 + 0.06 * sin(frame.time * 1.8);
  let d = length(uv) - radius;
  let aa = 1.5 / frame.resolution.y;
  let inside = 1.0 - smoothstep(-aa, aa, d);

  return vec4f(mix(vec3f(0.05, 0.07, 0.10), vec3f(0.13, 0.83, 0.93), inside), 1.0);
}

Bilinçli hata denemeleri

binding

@binding(0) sil

Beklenen sonuç: resource slot eksik olduğu için diagnostics konuşur.

type

data[i] = id.x; yaz

Beklenen sonuç: data f32, id.x ise u32.

Çalışma döngüsü

  1. Fonksiyon gövdesinden önce declaration'ları oku; kaynakları ve entry point'leri adlandır.
  2. Studio'da diagnostics, reflection ve bind-group layout'un tahmininle uyuştuğunu kontrol et.
  3. Tek invocation trace et; hangi değer lane'e özel, hangi değer paylaşılıyor ayır.
  4. WebGPU'da çalıştır; readback veya preview sonucunu tahmininle karşılaştır.
  5. Bir şeyi bilerek boz; compiler'ın hangi sınırı tarif ettiğini oku.