Kontrol yapıları, davranış analizi, assertion mekanizması, fonksiyonlar ve alias analizi.
9 Statements
Statement (ifade), programın yürütülmesini kontrol eden bir program parçasıdır. Statement'lar genellikle sıralı olarak çalıştırılır; ancak kontrol akışı (control flow) statement'ları programın sıralı olmayan şekilde yürütülmesine neden olabilir.
9.1 Compound Statement
Compound statement, süslü parantezlerle { }
çevrili sıfır veya daha fazla statement'tan oluşan bir bloktur.
{
var x: i32 = 0;
x = x + 1;
// x'in kapsamı bu blok sonunda biter
}
Kapsam kuralı: Bir compound statement içinde yapılan bildirim (declaration), bir sonraki statement'ın başlangıcından compound statement'ın sonuna kadar kapsam dahilindedir (in scope).
Not: continuing_compound_statement,
loop
statement'ının gövdesinin sonunda yer alan ve isteğe bağlı bir break if
ifadesine izin veren özel bir compound statement formudur.
9.2 Assignment Statement
Assignment (atama) ifadesi, bir ifadeyi değerlendirir ve isteğe bağlı olarak sonucu belleğe yazar (bir değişkenin içeriğini günceller).
Operatörün solundaki kısım sol taraf (left-hand side), sağındaki ifade ise sağ taraf (right-hand side) olarak adlandırılır.
9.2.1 Simple Assignment
Basit atama, sol tarafın bir ifade ve operatörün =
olduğu durumdur. Sağ tarafın değeri, sol tarafın referans verdiği bellek konumuna yazılır.
Ön koşullar:
e: T—Tconcrete constructible tip olmalır: ref<AS, T, AM>—ASyazılabilir adres uzayı,AMwriteveyaread_writeolmalı
struct S {
age: i32,
weight: f32
}
var<private> person: S;
fn f() {
var a: i32 = 20;
a = 30; // a'nın içeriğini 30 ile değiştir
person.age = 31; // person yapısının age alanına yaz
var uv: vec2<f32>;
uv.y = 1.25; // uv'nin ikinci bileşenine yaz
let uv_x_ptr: ptr<function, f32> = &uv.x;
*uv_x_ptr = 2.5; // Pointer üzerinden yazma
var sibling: S;
sibling = person; // Struct kopyalama
}
Not: Referans invalid memory reference ise, yazma işlemi gerçekleşmeyebilir veya beklenmeyen bir bellek konumuna yazılabilir.
9.2.2 Phony Assignment
Phony assignment, sol tarafın _
(underscore) token'ı olduğu durumdur. Sağ taraf değerlendirilir ancak sonucu saklanmaz.
_ = e // e değerlendirilir, sonucu atılır
_
bir identifier değildir, bu yüzden bir ifadede kullanılamaz.
Kullanım alanları:
- Dönüş değerini atmak: Değer döndüren bir fonksiyonu çağırıp sonucu kullanmamak:
var<private> counter: i32;
fn increment_and_yield_previous() -> i32 {
let previous = counter;
counter = counter + 1;
return previous;
}
fn user() {
// Sayacı artır ama sonucu kullanma
_ = increment_and_yield_previous();
}
- Kaynak bağlamalarını aktif etmek: Bir değişkeni statik erişimle (statically accessed) shader'ın resource interface'ine dahil etmek:
struct BufferContents {
counter: atomic<u32>,
data: array<vec4<f32>>
}
@group(0) @binding(0) var<storage> buf: BufferContents;
@group(0) @binding(1) var t: texture_2d<f32>;
@group(0) @binding(2) var s: sampler;
@fragment
fn shade_it() -> @location(0) vec4<f32> {
// buf, t ve s'yi shader arayüzünün parçası olarak bildir
_ = &buf; // Constructible olmayan tipler için pointer kullan
_ = t;
_ = s;
return vec4<f32>();
}
9.2.3 Compound Assignment
Compound assignment, işlem ve atamayı birleştiren kısa yol operatörleridir.
| Statement | Expansion | Açıklama | |||
|---|---|---|---|---|---|
e1 += e2
|
e1 = e1 + (e2)
|
Toplama ve atama | |||
e1 -= e2
|
e1 = e1 - (e2)
|
Çıkarma ve atama | |||
e1 *= e2
|
e1 = e1 * (e2)
|
Çarpma ve atama | |||
e1 /= e2
|
e1 = e1 / (e2)
|
Bölme ve atama | |||
e1 %= e2
|
e1 = e1 % (e2)
|
Kalan ve atama | |||
e1 &= e2
|
e1 = e1 & (e2)
|
Bitwise AND ve atama | |||
| `e1 \ | = e2` | `e1 = e1 \ | (e2)` | Bitwise OR ve atama | |
e1 ^= e2
|
e1 = e1 ^ (e2)
|
Bitwise XOR ve atama | |||
e1 >>= e2
|
e1 = e1 >> (e2)
|
Sağa kaydırma ve atama | |||
e1 <<= e2
|
e1 = e1 << (e2)
|
Sola kaydırma ve atama |
Önemli kurallar:
- Referans ifadesi
e1yalnızca bir kez değerlendirilir. e1için referans tipinin erişim moduread_writeolmalıdır.- Compound assignment, phony assignment ile
birleştirilemez (
_ += egeçersizdir). - Sağ taraf bağımsız bir ifade olarak
ayrıştırılır:
value = 2 + 3aslındavalue = value (2 + 3)demektir.
var<private> next_item: i32 = 0;
fn advance_item() -> i32 {
next_item += 1; // next_item'a 1 ekle
return next_item - 1;
}
fn bump_item() {
var data: array<f32, 10>;
next_item = 0;
// data[0]'a 5.0 ekle, advance_item() yalnızca BİR KEZ çağrılır
data[advance_item()] += 5.0;
// next_item burada 1 olacaktır
}
fn precedence_example() {
var value = 1;
value *= 2 + 3; // value = value * (2 + 3) = 5
}
Not: Referans
e1
bir kez değerlendirilse de, altta yatan belleğe iki kez
erişilir: önce eski değeri okumak için read access, sonra güncel değeri yazmak için write access.
9.3 Increment and Decrement Statements
Increment (++)
bir değişkenin içeriğine 1 ekler, decrement (--)
1 çıkarır.
Ön koşullar:
- İfade, concrete integer scalar (
i32veyau32) store type'a veread_writeerişim moduna sahip bir referansa çözümlenmelidir.
| Statement | Eşdeğer | Açıklama | |
|---|---|---|---|
r++
|
r += T(1)
|
Bellek içeriğine 1 ekle | |
r--
|
r -= T(1)
|
Bellek içeriğinden 1 çıkar |
fn f() {
var a: i32 = 20;
a++; // a = 21
a--; // a = 20
}
Not: ++
ve --
ifade (expression) değil, statement'tır. let b = a++
gibi kullanımlar WGSL'de geçersizdir. C/C++'daki gibi
prefix/postfix ayrımı yoktur.
9.4 Control Flow
Kontrol akışı statement'ları, programın sıralı olmayan şekilde yürütülmesine neden olabilir.
9.4.1 If Statement
if
ifadesi, koşul ifadelerinin değerlendirilmesine bağlı olarak en fazla bir compound statement'ı koşullu olarak çalıştırır.
Yapı: Bir if
cümlesi, ardından sıfır veya daha fazla else if
cümlesi, ardından isteğe bağlı bir else
cümlesi.
Tip kuralı: Her if
ve else if
cümlesindeki ifade bool
tipinde olmalıdır.
Yürütme akışı:
ifkoşulu değerlendirilir.trueise ilk compound statement çalışır.- Değilse, sıradaki
else ifkoşulları sırayla değerlendirilir; ilktrueolan çalışır. - Hiçbir koşul
truedeğilse veelsecümlesi varsa, onun gövdesi çalışır.
if condition {
// condition true ise
} else if other_condition {
// other_condition true ise
} else {
// hiçbiri değilse
}
9.4.2 Switch Statement
switch
ifadesi, bir seçici (selector) ifadesinin değerlendirmesine bağlı olarak kontrolü bir dizi case
cümlesinden birine veya default
cümlesine aktarır.
Kurallar:
- Her switch ifadesinde tam olarak bir
defaultcümlesi olmalıdır. - Selector ifadesi ve tüm case selector ifadeleri aynı concrete integer scalar tipinde olmalıdır.
- Case selector ifadeleri const-expression olmalıdır.
- Aynı switch'te iki farklı case selector ifadesi aynı değere sahip olmamalıdır.
- Case gövdesinin sonuna ulaşıldığında, kontrol switch ifadesinden sonraki ilk statement'a aktarılır (C'deki gibi fall-through yoktur).
var a: i32;
let x: i32 = generateValue();
switch x {
case 0: { // İki nokta isteğe bağlı
a = 1;
}
default { // default son olmak zorunda değil
a = 2;
}
case 1, 2, { // Birden fazla seçici değer; sondaki virgül isteğe bağlı
a = 3;
}
case 3 {
a = 4;
}
}
// default, başka case'lerle birleştirilebilir
const c = 2;
switch x {
case 0: { a = 1; }
case 1, c { a = 3; } // const-expression case seçicisi olarak kullanılabilir
case 3, default { a = 4; } // default anahtar kelimesi diğer case'lerle birleştirilebilir
}
9.4.3 Loop Statement
loop
ifadesi, bir loop body'yi (döngü gövdesi) tekrar tekrar
çalıştırır. Her çalıştırma bir iterasyon (iteration) olarak
adlandırılır.
Bu tekrarlama, bir break
veya return
ifadesiyle kesilebilir.
İsteğe bağlı olarak, loop gövdesinin son ifadesi bir continuing
ifadesi olabilir.
// Temel loop yapısı
var a: i32 = 2;
var i: i32 = 0;
loop {
if i >= 4 { break; }
a = a * 2;
i++;
}
// a = 32
// Loop + continuing + break if
var a: i32 = 2;
var i: i32 = 0;
loop {
let step: i32 = 1;
if i % 2 == 0 { continue; }
a = a * 2;
continuing {
i = i + step;
break if i >= 4; // continuing içinde koşullu break
}
}
Önemli kurallar:
- Sınırsız sayıda iterasyon çalıştırılırsa dynamic error oluşur. Bu, döngünün erken sonlandırılmasına veya device loss'a yol açabilir.
- Loop gövdesindeki bildirimler her iterasyonda yeniden oluşturulur ve yeniden başlatılır.
Not: loop
ifadesi WGSL'e özgü bir yapıdır. Çoğu durumda for
veya while
tercih edilmelidir. loop,
derlenmiş kodda yaygın bulunan döngü kalıplarını doğrudan ifade eder.
9.4.4 For Statement
for
ifadesi, bir loop
ifadesinin üzerine syntactic sugar (sözdizimsel kolaylık) sağlar.
Genel form:
for (initializer ; condition ; update_part) { body }
Eşdeğer loop dönüşümü (condition varsa):
{
initializer;
loop {
if !(condition) { break; }
body
continuing { update_part }
}
}
Kurallar:
- Initializer: Döngüden önce bir kez çalıştırılır. Bildirilen tanımlayıcı, döngü gövdesinin sonuna kadar kapsam dahilindedir. Her iterasyonda yeniden başlatılmaz.
- Condition: Bool tipinde olmalıdır. Her iterasyonun başında
değerlendirilir;
falseise döngüden çıkılır. - Update part: Her iterasyonun sonunda continuing ifadesi olarak çalışır.
- Body: Özel bir compound statement formu. Gövde içindeki bildirimler her iterasyonda yeniden oluşturulur.
var a: i32 = 2;
for (var i: i32 = 0; i < 4; i++) {
a *= 2;
}
// a = 32
// Koşul olmadan da kullanılabilir (sonsuz döngü):
for (var i: i32 = 0; ; i++) {
if i == 4 { break; }
a = a + 2;
}
9.4.5 While Statement
while
ifadesi, bir koşula bağlı döngüdür. Her iterasyonun başında boolean koşul değerlendirilir; false
ise döngü sona erer.
Koşul bool
tipinde olmalıdır.
Aşağıdaki üç form birbirine eşdeğerdir:
while condition { body }loop { if !condition { break; } body }for (; condition ;) { body }
var i: i32 = 0;
while i < 10 {
// ...
i++;
}
- Sınırsız sayıda iterasyon → dynamic error (device loss olabilir).
9.4.6 Break Statement
break
ifadesi, kontrolü en yakın çevreleyen döngü veya switch
ifadesinin hemen sonrasına aktarır, böylece o yapının yürütülmesini sonlandırır.
Kurallar:
- Yalnızca
loop,for,whileveswitchifadeleri içinde kullanılabilir. continuingbloğundan döngüyü sonlandırmak içinbreakkullanılamaz. Bunun yerinebreak ifkullanılmalıdır.
loop {
// ...
continuing {
if i >= 4 { break; } // Geçersiz! continuing içinde break olmaz
}
}
9.4.7 Break-If Statement
break if
ifadesi bir boolean koşul değerlendirir; koşul true
ise en yakın çevreleyen loop'un
hemen sonrasına kontrolü aktarır.
Kurallar:
- Koşul
booltipinde olmalıdır. - Yalnızca bir
continuingbloğunun son ifadesi olarak kullanılabilir.
var a: i32 = 2;
var i: i32 = 0;
loop {
let step: i32 = 1;
if i % 2 == 0 { continue; }
a = a * 2;
continuing {
i = i + step;
break if i >= 4; // Geçerli: continuing'in son ifadesi
}
}
9.4.8 Continue Statement
continue
ifadesi, en yakın çevreleyen döngüde kontrolü aktarır:
- Eğer bir
continuingbloğu varsa → oraya atlar (ileri dallanma). - Yoksa → döngü gövdesinin başına geri döner (sonraki iterasyon).
Kurallar:
- Yalnızca
loop,forvewhileiçinde kullanılabilir. - Çevreleyen bir
continuingbloğuna kontrol akışı aktaramaz (kendi continuing'ine doğrudan atlamak yasak). - Hedeflenen
continuingbloğunda kullanılan bir bildirimden sonra atlanamaz (o bildirim bypass edilemez).
// Geçersiz: continue, step bildirimini atlıyor ama continuing bunu kullanıyor
var i: i32 = 0;
loop {
if i >= 4 { break; }
if i % 2 == 0 { continue; } // step'in tanımını atlıyor
let step: i32 = 2;
continuing {
i = i + step; // step burada kullanılıyor → hata!
}
}
9.4.9 Continuing Statement
continuing
ifadesi, bir döngü iterasyonunun sonunda çalıştırılacak bir compound statement belirtir. İsteğe bağlıdır.
Kısıtlama: continuing
bloğu içinde hiçbir iç içe geçme seviyesinde return
ifadesi kullanılamaz.
loop {
// loop body
continuing {
// Her iterasyonun sonunda çalışır
// (continue veya normal akış ile ulaşılır)
// NOT: Burada return kullanılamaz!
}
}
9.4.10 Return Statement
return
ifadesi, mevcut fonksiyonun yürütülmesini sonlandırır.
- Fonksiyon bir entry point ise, shader invocation'ı sonlandırılır.
- Değilse, çağrı noktasından (call site) sonraki ifade/statement'a devam edilir.
Kurallar:
- Fonksiyonun dönüş tipi yoksa:
returnisteğe bağlıdır. Sağlanırsa değer içermemelidir. - Fonksiyonun dönüş tipi varsa:
returnifadesi zorunludur ve dönüş değeri fonksiyonun dönüş tipiyle eşleşmelidir.
fn add(a: f32, b: f32) -> f32 {
return a + b; // Dönüş değeri zorunlu
}
fn do_something() {
// ...
return; // Dönüş değeri yok (isteğe bağlı)
}
9.4.11 Discard Statement
discard
ifadesi, mevcut invocation'ı bir helper invocation'a dönüştürür ve
fragment'ı atar. Yalnızca fragment shader aşamasında
kullanılabilir.
Etkiler:
- Invocation, helper invocation'a dönüştürülür.
- Mevcut fragment, GPU render pipeline'ında daha ileri işlenmez.
- Yalnızca
discard'dan önce çalıştırılan statement'lar gözlemlenebilir etkilere sahip olacaktır.
@group(0) @binding(0)
var<storage, read_write> will_emit_color: u32;
fn discard_if_shallow(pos: vec4<f32>) {
if pos.z < 0.001 {
// Bu çalıştırılırsa, will_emit_color asla 1 olarak ayarlanmaz
// çünkü helper invocation'lar paylaşımlı belleğe yazmaz
discard;
}
will_emit_color = 1;
}
@fragment
fn main(@builtin(position) coord_in: vec4<f32>)
-> @location(0) vec4<f32>
{
discard_if_shallow(coord_in);
will_emit_color = 1;
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}
Not: discard,
fragment aşamasındaki herhangi bir fonksiyonda çalıştırılabilir (yalnızca entry point'te değil) ve etki
aynıdır: fragment atılır.
9.5 Function Call Statement
Fonksiyon çağrı ifadesi, bir fonksiyon çağrısını çalıştırır. Dönüş değeri olan bir fonksiyon çağrılıyorsa
ve fonksiyonda @must_use
attribute'u varsa, sonucu atmak shader-creation error oluşturur.
do_work(); // Dönüş tipi olmayan fonksiyon çağrısı
_ = compute_value(); // Dönüş değeri açıkça atılır (must_use yoksa)
Not: Fonksiyon
bir değer döndürüyor ve @must_use
attribute'u yoksa, dönüş değeri sessizce yok sayılır.
9.6 Statements Grammar Summary
statement :
| ';' // Boş statement
| return_statement ';'
| if_statement
| switch_statement
| loop_statement
| for_statement
| while_statement
| func_call_statement ';'
| variable_or_value_statement ';'
| break_statement ';'
| continue_statement ';'
| 'discard' ';'
| variable_updating_statement ';'
| compound_statement
| assert_statement ';'
variable_updating_statement :
| assignment_statement
| increment_statement
| decrement_statement
Özel bağlam gerektiren statement'lar:
break_if_statement— yalnızcacontinuingbloğunun son ifadesi olarakcontinuing_compound_statement— yalnızcaloopgövdesinin son ifadesi olarak
9.7 Statements Behavior Analysis
Behavior analysis (davranış analizi), her statement'ın yürütülmesi tamamlandıktan sonra kontrolün nasıl devam edeceğini özetleyen bir sistemdir. Bu analiz, hem kontrol akışı kurallarının geçerliliğini doğrulamak hem de uniformity analysis (15.2) için kullanılır.
9.7.1 Rules
Bir behavior (davranış), aşağıdaki elemanlardan oluşabilen bir kümedir:
| Behavior Elemanı | Anlamı | |
|---|---|---|
| Return | Fonksiyon, return
ile sonlanır |
|
| Break | Döngü/switch, break
ile sonlanır |
|
| Continue | Döngü, continue
ile sonraki iterasyona geçer |
|
| Next | Bir sonraki statement'a düşer (fall-through) |
Fonksiyon kuralları:
- Dönüş tipi olan fonksiyonun gövdesi {Return} behavior'una sahip olmalıdır (tüm yollarda return olmalı).
- Dönüş tipi olmayan fonksiyonun gövdesi {Next, Return}'ün bir alt kümesi olmalıdır.
Temel statement behavior'ları:
| Statement | Sonuç Behavior | |
|---|---|---|
Boş statement
(;)
|
{Next} | |
break;
|
{Break} | |
continue;
|
{Continue} | |
return;
/ return e;
|
{Return} | |
discard;
|
{} | |
| Atama, increment, decrement | {Next} | |
| Fonksiyon çağrısı | Fonksiyonun behavior'u |
Bileşik behavior kuralları:
- Sıralı
compound:
{ s1; s2; }→ İlk statement'ın Next olmayan behavior'ları + ikinci statement'ın behavior'u. - if/else:
Tüm dalların behavior birleşimi.
elseyoksa {Next} eklenir. - switch: Tüm case'lerin behavior birleşimi (Break → Next'e dönüştürülür).
- loop: Body behavior'undaki Continue → Next'e, Break → Next'e dönüştürülür. Next kaldırılır (sonsuz döngü anlamına gelir).
- for/while: Loop behavior'una benzer, ancak koşul false olabilir → {Next} eklenir.
9.7.2 Notes
- Behavior analizi statik analiz olduğundan, dead code (erişilemeyen kod) derleme hataları üretebilir.
- Bir fonksiyonun behavior'u, gövde behavior'undaki her "Return" yerine "Next" konularak hesaplanır.
- Sonuç olarak bir fonksiyonun behavior'u her
zaman
{}(hiç dönmez) veya{Next}(normal dönüş) olur.
9.7.3 Examples
// Behavior: {Return} — dönüş tipi olan fonksiyon için geçerli
fn valid_return(x: i32) -> i32 {
if x > 0 {
return x; // {Return}
} else {
return -x; // {Return}
}
// if/else behavior: {Return} ∪ {Return} = {Return}
}
// Hata: Behavior {Next, Return} — tüm yollarda return yok
fn invalid_return(x: i32) -> i32 {
if x > 0 {
return x; // {Return}
}
// else yok → {Next} eklenir
// Sonuç: {Next, Return} — dönüş tipi olan fonksiyon için geçersiz!
}
// Behavior: {Next, Return} — dönüş tipi olmayan fonksiyon için geçerli
fn valid_void(x: i32) {
if x > 0 {
return; // {Return}
}
// {Next} — sorun yok
}
10 Assertions
Assertion (doğrulama), bir boolean koşulun sağlandığını garanti eden bir kontroldür.
10.1 Const Assertion Statement
const_assert
ifadesi, koşul false
olarak değerlendirilirse shader-creation error üreten bir
doğrulamadır.
Kurallar:
- İfade
booltipinde olmalıdır. - İfade bir const-expression olmalıdır.
- Hem module scope hem de function scope'ta kullanılabilir.
- Derlenmiş shader üzerinde hiçbir etkisi yoktur (yalnızca derleme zamanı kontrolü).
const WORKGROUP_SIZE = 256;
const MAX_SIZE = 1024;
// Module scope'ta kullanım
const_assert WORKGROUP_SIZE <= MAX_SIZE;
const_assert WORKGROUP_SIZE % 32 == 0;
fn foo() {
const x = 1;
const y = 2;
const z = x + y - 2;
const_assert z > 0; // Geçerli: z const-expression
let a = 3;
// const_assert a != 0; // Geçersiz: a const-expression değil (let)
}
Parantezler isteğe bağlıdır:
const_assert x < y; // Parantez yok
const_assert(y != 0); // Parantez ile
Kullanım senaryoları:
- Derleme zamanında yapılandırma parametrelerini doğrulama
- Workgroup boyutlarının sınırlar dahilinde olduğunu kontrol etme
- Template/const parametrelerinin tutarlılığını garanti etme
11 Functions
Fonksiyon, çağrıldığında hesaplama işi gerçekleştiren bir birimdir. WGSL'de iki tür fonksiyon vardır:
- Built-in fonksiyonlar: WGSL implementasyonu tarafından sağlanır, her zaman kullanılabilir (17).
- Kullanıcı tanımlı fonksiyonlar: WGSL modülünde bildirilir.
Çağırma yolları:
- Fonksiyon çağrı ifadesi ile (8.10 — dönüş değeri olan)
- Fonksiyon çağrı statement'ı ile (9.5 — dönüş değeri yok sayılan)
- Entry point olarak WebGPU implementasyonu tarafından (13)
Not: Fonksiyonlar kaynak kodda herhangi bir sırada tanımlanabilir; forward declaration gerekmez.
11.1 Declaring a User-defined Function
Fonksiyon bildirimi aşağıdaki unsurları belirtir:
- İsteğe bağlı attribute'lar (ör.
@vertex,@fragment,@compute,@workgroup_size) - Fonksiyonun adı
- Sıralı formal parametre listesi (virgülle ayrılmış, parantez içinde)
- İsteğe bağlı dönüş tipi (attribute'larla birlikte)
- Fonksiyon gövdesi (çağrıldığında çalıştırılacak statement'lar)
Kurallar:
- Fonksiyon bildirimleri yalnızca module scope'ta yapılabilir.
- Fonksiyon adı tüm program boyunca kapsam dahilindedir.
- Her kullanıcı tanımlı fonksiyonun yalnızca bir overload'u vardır.
- Dönüş tipi belirtilirse, constructible tip olmalıdır.
- Parametrelerin tipleri: constructible, pointer, texture veya sampler olabilir.
- İki formal parametre aynı ada sahip olmamalıdır.
// Basit fonksiyon: iki parametre (i32 ve f32), i32 döndürür
fn add_two(i: i32, b: f32) -> i32 {
return i + 2; // Formal parametre gövdede kullanılabilir
}
// Compute shader entry point
@compute @workgroup_size(1)
fn main() {
let six: i32 = add_two(4, 5.0);
}
Parametre ve dönüş tipi attribute'ları:
| Attribute | Uygulama Alanı | Açıklama | |
|---|---|---|---|
@builtin
|
Parametre / Dönüş | Built-in değer (ör. position,
global_invocation_id)
|
|
@location
|
Parametre / Dönüş | Inter-stage veya output location | |
@blend_src
|
Dönüş | Dual-source blending kaynağı | |
@interpolate
|
Parametre / Dönüş | Interpolasyon modu | |
@invariant
|
Dönüş | Pozisyon çıkışı değişmezliği |
Fonksiyon attribute'ları:
| Attribute | Açıklama | |
|---|---|---|
@vertex
|
Vertex shader entry point | |
@fragment
|
Fragment shader entry point | |
@compute
|
Compute shader entry point | |
@workgroup_size(x, y, z)
|
Compute shader workgroup boyutu |
11.2 Function Calls
Fonksiyon çağrısı, bir fonksiyonu çalıştıran bir statement veya ifadedir.
Terminoloji:
- Calling function (caller): Çağrıyı yapan fonksiyon
- Called function (callee): Çağrılan fonksiyon
- Call site: Çağrının kaynak koddaki konumu
Çağrı kuralları:
- Argüman sayısı, formal parametre sayısıyla eşleşmelidir.
- Her argüman tipi, karşılık gelen parametrenin tipiyle uyumlu olmalıdır.
- Argüman değerlendirme sırası: soldan sağa.
Çağrı yürütme adımları:
- Argüman değerleri soldan sağa değerlendirilir.
- Çağıran fonksiyon askıya alınır (tüm yerel değişkenler ve sabitler durumlarını korur).
- Çağrılan fonksiyon kullanıcı tanımlı ise, function scope değişkenleri için bellek ayrılır ve başlatılır.
- Formal parametrelere, çağrı argümanları pozisyona göre eşlenir.
- Kontrol çağrılan fonksiyona aktarılır (gövdenin ilk statement'ından başlar).
- Çağrılan fonksiyon dönene kadar çalıştırılır.
- Kontrol çağıran fonksiyona geri aktarılır. Dönüş değeri varsa, çağrı ifadesinin değeri olarak sağlanır.
Dönüş koşulları:
- Built-in: İşi tamamlandığında döner.
- Dönüş tipli
kullanıcı tanımlı:
returnstatement'ı çalıştırıldığında. - Dönüş tipi
olmayan kullanıcı tanımlı:
returnveya gövdenin sonuna ulaşıldığında.
Not: Fragment shader'da bir çağrıdaki tüm quad invocation'lar discard edilmişse, fonksiyon çağrısı asla dönmeyebilir.
11.3 const
Functions
@const
attribute'u ile bildirilen bir fonksiyon, shader-creation
zamanında (derleme zamanı) değerlendirilebilir. Bu fonksiyonlara const-function denir.
- Const-function çağrıları, const-expression'ların parçası olabilir.
- Fonksiyonun gövdesindeki tüm ifadeler const-expression olmalı ve tüm bildirimler const-declaration olmalıdır.
Önemli: @const
attribute'u kullanıcı tanımlı fonksiyonlara uygulanamaz.
Yalnızca built-in fonksiyonlar const-function olabilir.
// firstLeadingBit bir const-function'dır (built-in)
const first_one = firstLeadingBit(1234 + 4567); // Değer: 12, Tip: i32
@id(1) override x: i32;
override y = firstLeadingBit(x); // override-expression olarak kullanılabilir
// (bu bağlamda const-expression DEĞİL)
fn foo() {
// Dizi boyutu olarak const-expression'da kullanılabilir
var a: array<i32, firstLeadingBit(257)>;
}
11.4 Restrictions on Functions
WGSL fonksiyonları üzerinde katı kısıtlamalar uygulanır:
| Kısıtlama | Açıklama | |
|---|---|---|
| Vertex shader | position
built-in output value döndürmelidir |
|
| Entry point | Fonksiyon çağrısının hedefi olamaz (yalnızca WebGPU tarafından çağrılır) | |
| Dönüş tipi | Constructible tip olmalıdır | |
| Parametre tipi | Constructible, pointer, texture veya sampler olmalıdır | |
| Argüman tipi | Her argüman, karşılık gelen parametrenin tipiyle eşleşmelidir | |
| Pointer argüman | Address space, store type ve access mode eşleşmelidir | |
| Rekürsiyon | Bildirimler arasında döngüsel bağımlılıklar yasaktır |
fn bar(p: ptr<function, f32>) { }
fn baz(p: ptr<private, i32>) { }
fn baz2(p: ptr<storage, f32>) { }
@group(0) @binding(0) var<storage> ro_storage: f32;
@group(0) @binding(1) var<storage, read_write> rw_storage: f32;
fn foo() {
var usable_func: f32;
var i32_func: i32;
bar(&usable_func); // Geçerli: function address space, f32
baz(&i32_func); // Geçersiz: address space uyumsuzluğu (function vs private)
baz2(&ro_storage); // Geçerli: storage, read, f32
baz2(&rw_storage); // Geçersiz: access mode uyumsuzluğu (read_write vs read)
}
Not: Rekürsiyon, bildirimler arasında döngüsel bağımlılıklara izin verilmediği için yasaktır.
11.4.1 Alias Analysis
WGSL, alias analizi kurallarıyla yazma-yazma ve okuma-yazma çakışmalarını önler. Bu analiz, aynı bellek konumuna birden fazla yoldan erişimi kısıtlar.
11.4.1.1 Root Identifier
Bir fonksiyon içinde her memory view (referans veya pointer) belirli bir root identifier'a sahiptir. Root identifier, o belleğe ilk erişimi sağlayan değişken veya pointer parametresidir.
Root identifier belirleme kuralları:
| İfade formu | Root identifier | |
|---|---|---|
Değişken adı v
|
v
kendisi |
|
Pointer parametresi
p
|
p
kendisi |
|
let x = E2
→ x
kullanımı |
E2'nin
root identifier'ı |
|
(E2),
&E2,
*E2,
E2[i]
|
E2'nin
root identifier'ı |
|
E2.member,
E2.swizzle
|
E2'nin
root identifier'ı |
11.4.1.2 Aliasing
İki root identifier, aynı originating variable'a sahip olduğunda alias oluştururlar.
Kural: Bir WGSL fonksiyonunun yürütülmesi, alias olan root identifier'lar aracılığıyla belleğe potansiyel erişim yapmamalıdır — eğer bu erişimlerden biri yazma ve diğeri okuma veya yazma ise.
Call site kuralları: Her fonksiyon çağrısında aşağıdakiler shader-creation error oluşturur:
- Aynı root identifier'a sahip iki pointer argümanı ve karşılık gelen parametrelerden biri yazma kümesindeyse.
- Root identifier'ı module-scope değişkenine eşlenen bir pointer argümanı ve karşılık gelen parametre yazma kümesindeyken, aynı module-scope değişkeni çağrılan fonksiyonda okunuyorsa.
- Root identifier'ı module-scope değişkenine eşlenen bir pointer argümanı ve karşılık gelen parametre okuma kümesindeyken, aynı module-scope değişkeni çağrılan fonksiyonda yazılıyorsa.
var<private> x: i32 = 0;
fn f1(p1: ptr<function, i32>, p2: ptr<function, i32>) {
*p1 = *p2; // p1 yazılır, p2 okunur
}
fn f4(p1: ptr<function, i32>, p2: ptr<function, i32>) -> i32 {
return *p1 + *p2; // Her ikisi de sadece okunur
}
fn f6(p: ptr<private, i32>) {
x = *p; // x yazılır (global), p okunur
}
fn f7(p: ptr<private, i32>) -> i32 {
return x + *p; // İkisi de sadece okunur
}
fn f3() {
var a: i32 = 0;
f1(&a, &a); // Geçersiz: aynı root identifier, biri yazılıyor
}
fn f5() {
var a: i32 = 0;
let b = f4(&a, &a); // Geçerli: her ikisi de sadece okunuyor
}
fn f8() {
let a = f6(&x); // Geçersiz: x hem global yazma hem parametre okuma
let b = f7(&x); // Geçerli: x yalnızca okunuyor (global + parametre)
}