HTML5 Canvas · 2D–WebGL köprüsü
CPU işlemesi ve GPU işlemesi: Canvas geliştiricisi için rol haritası
Tarayıcı oyununda «iş» hem ana iş parçacığında ( CPU üzerinde çalışan JavaScript ve düzen motoru ) hem grafik işlemcisinde ( GPU üzerinde yürütülen raster ve sayfadırıcı işleri ) paylaşılır. Canvas 2D ile başlayan geliştirici için kritik fark: komutları yazdığınız yer ile piksellerin gerçekten boyandığı yer aynı kod satırında görünmez — köprüye geçerken bu ayrım netleşmelidir. Bu sayfa genel donanım dersi vermez; odak Canvas 2D bağlamı ile WebGL bağlamının masraflarını karşılaştırmak ve doğru API seçimini gerekçelendirmektir.
Piksel başına maliyet ve bağlam sıçraması Yeniden çizim maliyeti ile işlenir; çizim çağrılarının sırası Batching mantığı başlığında kalır — burada tekrar edilmez. Geniş özet için köprü okuması: GPU vs CPU. Komşu köprü konuları: İş hattı farkları, Canvas sınırları, Neden WebGL?
Özet: iki bağlamda iş kimde?
| Konu | CPU tarafı | GPU tarafı |
|---|---|---|
| Canvas 2D çizimi | Komut üretimi, stil ve sıra | Raster / bileşim ( uygulamaya bağlı) |
| WebGL çizimi | Tampon yükleme, durum, çağrı | Vertex / fragment programları |
| Geri okuma ( readback ) | Sonuç JS belleğine aktarılır | Senkron beklemeye yol açabilir |
Rol ayrımı ve tarayıcı içindeki iki dünya
CPU üzerinde çalışan kodunuz çizim kararını üretir: hangi sprite nerede, hangi renk, hangi yüzey güncellenmeli. Bu kararlar Canvas 2D’de çoğu zaman tek tek çağrıya dökülür; WebGL’de ise tamponlara yazılmış geometri ve örnekleyici tarafından tetiklenir. GPU ise aynı anda çok sayıda piksel veya köşe üzerinde paralel iş yükünü yürütmeye uygundur — fakat bu paralellik «her iş GPU’da bedavadır» anlamına gelmez; veriyi GPU’ya taşımak, durumu kurmak ve senkron beklemek yine CPU zamanınızdan yer.
Canvas 2D uygulaması yazarken zihinsel model çoğu zaman «tuvala komut yazıyorum» şeklindedir; WebGL’ye geçişte model «durum makinesi + gönder ( dispatch )» haline gelir. Köprü öğrenmesi: aynı sahneyi düşünün — birinde çağrı sırası okunabilirlik sunar, diğerinde veri hazırlığı ve gölgelendirici ( shader ) kontratı öne çıkar. Hangi yaklaşımın daha az sürdürülebilir olduğu projeye bağlıdır; bu sayfa «WebGL her zaman kazanır» demez.
Tarayıcı ve işletim sistemi sürücüleri arasında ek katmanlar vardır; uygulama kodunuz doğrudan donanımı yönetmez. Bu yüzden mikro düzeyde «şu komut GPU’da şu kadar döngü» iddiası yerine ölçüm ve köprü kavramları ile ilerlemek daha güvenilirdir.
Canvas 2D ve hemen çizim düşüncesinin maliyeti
Canvas 2D API’si geliştiriciye «şimdi şunu çiz» hissi verir; motor ise bu çağrıları içte işleyip birleştirilmiş çıktı üretir. Maliyet iki eksende büyür: işlediğiniz piksel sayısı ve bağlam durumunun ne sıklıkla değiştiği ( dolgu, süzgeç, birleştirme modu ). Bu ikinci eksen bağlam maliyeti başlığında sabitlenmiştir — CPU tarafında sık özellik değişimi genelde daha görünür bir profil imzası üretir.
Canvas 2D birçok kullanım için yeterince hızlıdır: kullanıcı arayüzleri, basit 2D oyunlar, grafik çizimleri. Köprü sorusu şudur: «aynı sahneyi daha fazla öznitelik ve shader kontrolü ile sürdürebilir miyim?» — hayır ise WebGL düşünmek için tek başına CPU/GPU ayrımı yetmez; sahne karmaşıklığı ve ürün yol haritası gerekir.
Bellekte ikinci bir tampon ( Offscreen Canvas ) kullanmak CPU tarafında hazırlığı artırır fakat ana döngüde raster süresini doğru tasarlandığında düşürebilir — bu, GPU yerine «işin nerede yapıldığını seçme» örneğidir.
WebGL gönderim modeli ve CPU’nun hazırlık rolü
WebGL’de çizim tipik olarak şöyle akar: tamponlar (
konum, doku koordinatı vb. ) GPU belleğine veya paylaşılan belleğe yüklenir, gölgelendirici
programı seçilir, birleştirme ve derinlik durumu ayarlanır, ardından
drawArrays /
drawElements (
veya örneklenmiş varyantlar ) ile gönderim yapılır. Bu gönderimin her biri CPU tarafından
tetiklenir; yani «GPU çiziyor» doğru olsa da «CPU hiç çalışmıyor» yanlıştır.
Köprü için kritik içgörü: WebGL performansı çoğu zaman çağrı sayısı ve durum değişimi ile sınırlanır — Canvas 2D’deki bağlam sıçraması ile aynı sınıf düşünce, farklı API yüzeyi. Bu yüzden WebGL tarafında da toplu geometri ( batching ), örneklenmiş çizim ( instancing ) ve dokuların yeniden kullanımı önemlidir; detay Three.js veya ham WebGL kurslarında genişler — burada yalnızca CPU hazırlığı ↔ GPU yürütmesi dengesi işaretlenir.
Aşağıdaki yardımcı, sayfada halihazırda var olan bir tuval öğesi üzerinde WebGL bağlamı açılıp açılamayacağını güvenli şekilde dener; oluşturucuya kullanıcı girdisi bağlanmaz. Üretimde bağlam kaybı ve güç tercihi ( powerPreference ) politikanıza göre ayarlanır.
/**
* WebGL bağlamı güvenli dene — şirket içi politikaya göre seçenekleri daraltın.
* @param {HTMLCanvasElement} canvas
* @returns {WebGLRenderingContext | WebGL2RenderingContext | null}
*/
export function tryWebGLContext(canvas) {
if (!(canvas instanceof HTMLCanvasElement)) return null;
const opts = {
alpha: true,
antialias: true,
depth: true,
stencil: false,
powerPreference: 'default',
premultipliedAlpha: true,
preserveDrawingBuffer: false,
failIfMajorPerformanceCaveat: false,
};
try {
return (
canvas.getContext('webgl2', opts) ||
canvas.getContext('webgl', opts) ||
canvas.getContext('experimental-webgl', opts)
);
} catch {
return null;
}
}
Paralellik: GPU’nun güçlü ve zayıf uyduğu işler
GPU, aynı gölgelendirici programını çok sayıda piksel veya köşe üzerinde paralel yürütmeye uygundur; bu nedenle tam ekran efektler, yoğun parçacık benzeri çıktılar ve tekrarlayan örüntüler için çekicidir. Ancak dallanmalı ( branchy ) mantık ( her pikselde farklı koşul ) paralelliği kırabilir; düzensiz bellek erişimi ( önbellek dostu olmayan örnekleme ) verim düşürür. Bu «uygun iş yükü» düşüncesi, Canvas 2D’den WebGL’ye geçiş kararında sahneyi tanımlamak için gereklidir.
CPU tarafında ise düzensiz mantık, sahne grafiği gezintisi ve giriş işleme doğaldır. Köprü kararı: yoğun paralel raster işini GPU programına taşıyıp CPU’yu düşük frekanslı simülasyon ve hazırlığa ayırmak — fakat taşımanın bedeli gölgelendirici yazımı, tampon yaşam döngüsü ve hata ayıklama maliyetidir.
Tek başına «GPU daha hızlıdır» ifadesi yanıltıcıdır; küçük geometri ve az çağrı ile WebGL, sabit durum maliyeti yüzünden beklenenden yavaş görünebilir. Ölçüm ve sahne büyüklüğü olmadan araç seçimi yapılmamalıdır.
Veri yolu: yükleme, dokular ve Canvas ile köprü
GPU’ya veri göndermek bant genişliği ve düzen (
hizalama, sıkıştırma ) ile ilgilidir. Decode edilmiş görüntüler WebGL dokusu olarak
yüklenir;
Canvas 2D’de ise drawImage ile doğrudan bitmap kaynak kullanılabilir. Köprü
senaryosu: aynı decode edilmiş görsel hem 2D katmanda hem WebGL dokusu olarak yaşayabilir —
çift tutmanın bellek maliyeti vardır; tek kaynakta birleştirmek için ortak yükleme
politikası
gerekir.
Görsel
yükleme
akışı burada tekrar edilmez.
Her karede büyük tamponları yeniden yüklemek ( streaming hariç bilinçli tasarım ) maliyetlidir — CPU hazırlığı ve veri yolu trafiği artar. Bu yüzden WebGL tarafında «dinamik güncellenen dokular» ile Canvas 2D’de «her kare tam boy güncelleme» benzer şekilde şişebilir; köprü mimarisinde hangi verinin ne sıklıkta taşındığını belgelemek önemlidir.
Aşağıdaki örnek, küçük bir yardımcı tuval üzerinde bir kerelik bağlam sondası yapar; ana görünür tuvalinize dokunmaz. Sonuç önbelleğe alınır — çağrıyı uygulama başında bir kez yapın; her kare çağırmak bağlam oluşturma maliyeti doğurur ( üretimde tek seferlik özellik algılama kalıbı ).
/** @type {{ webgl2: boolean, webgl1: boolean, d2: boolean } | null} */
let cachedProbe = null;
/**
* Uygulama başında bir kez çağrın; dahili 1×1 tuvalde bağlam dener (
* görünür tuvalinize dokunmaz ).
*/
export function probeGraphicsBackendsOnce() {
if (cachedProbe) return cachedProbe;
const out = { webgl2: false, webgl1: false, d2: false };
try {
const probe = () => {
const c = document.createElement('canvas');
c.width = 1;
c.height = 1;
return c;
};
out.webgl2 = !!probe().getContext('webgl2');
const c1 = probe();
out.webgl1 = !!(c1.getContext('webgl') || c1.getContext('experimental-webgl'));
out.d2 = !!probe().getContext('2d');
} catch {
/* algılama başarısız — güvenli varsayılanlar */
}
cachedProbe = out;
return out;
}
Okuma geri senkronu ve ana iş parçacığında takılma
GPU’dan CPU’ya veri geri okumak (
readback ) genelde pahalıdır: boru hattı tamamlanmayı bekleyebilir,
ana iş parçacığında ani süre sıçramaları görülebilir. Canvas 2D’de getImageData
ve WebGL’de readPixels bu sınıftır — her karede tam ekran okuma nadiren
sürdürülebilir.
Köprü tasarımında «GPU’da hesaplayıp CPU’da her kare tüketmek» yerine sonucu mümkün
olduğunca
GPU üzerinde tutmak veya okumayı seyrekleştirmek tipik rahatlamadır.
Bu kısıt, Canvas ile WebGL birlikte kullanılan hibrit düzenlerde özellikle önemlidir: örneğin WebGL çıktısını her karede piksel piksel işlemek için CPU’ya aktarmak köprünün amacını boşa çıkarabilir. İş birimi olarak dokular ve tamponlar üzerinden düşünmek ve okuma noktalarını belgelemek gerekir.
Kare bütçesi ölçümü için deterministik bir zaman çerçevesi iskeleti (
aşağıda ) kullanılabilir; gerçek zamanlayıcı politikası (
performance.now,
rAF ) projenize bağlıdır — örnek doğrudan kullanıcı girdisi içermez.
/**
* Kare içi işleri üst süre ile sınırlamak için basit sayaç (
* ölçüm / erken çıkış kalıbı — gerçek zaman kaynağı çağıran ekler).
* @param {number} maxMs - Pozitif milisaniye üst sınırı
*/
export function createFrameBudget(maxMs) {
const cap = Math.max(0.5, Number(maxMs) || 16);
let start = 0;
return {
begin(nowMs) {
start = Number(nowMs) || 0;
},
/** Kalan süre; 0 veya negatif ise süre dolmuş demektir. */
remaining(nowMs) {
const now = Number(nowMs) || start;
return cap - (now - start);
},
capMs() {
return cap;
},
};
}
Araç seçimi özeti, tuzaklar ve kontrol listesi
Canvas 2D: düşük sürtümeli prototip, doğrudan çizim, 2D odaklı ürünler için güçlüdür. WebGL: özel gölgelendirici, derinlik / stencil, çok öğeli 3D veya yoğun paralel raster gereksinimi için güçlüdür. Köprü kararı salt «CPU mu GPU mu» değil; sürdürülebilirlik ve ekip yetkinliği de tartıya girer.
Yaygın tuzaklar: WebGL’yi az geometri ile kullanıp sabit maliyet ödemek; her kare GPU→CPU okuma yapmak; Canvas 2D’de pahalı süzgeci her sprite için açık unutmak ( bağlantılı başlık üzerinden özetlenmiştir ); köprü kodunda çift bağlamı koordinasyonsuz bırakmak. Bu liste Canvas sınırları ve Neden WebGL? ile birlikte tamamlanır.
probeGraphicsBackendsOnce dahili yardımcı tuvalde bağlam dener; ana tuvalinizi
kirletmez — yine de sıklığı düşük tutun (
önbellek tek seferlik kullanım içindir ).
Bu sayfanın sınırı
Vulkan, Metal veya doğrudan sürücü düzeyi optimizasyonları işlenmez. WebGPU ayrı başlık olarak haritada yer alır; bu sayfa Canvas geliştiricisinin WebGL köprüsüne hazırlığı için CPU/GPU rol ayrımını sabitler.
- Profilde darboğaz CPU hazırlığı mı, GPU süresi mi, okuma geri senkronu mu?
- Canvas 2D bağlam sıçraması ve piksel bütçesi ölçüldü mü?
- WebGL tarafında gönderim ve durum değişimi sınırlı mı?
- Her kare okuma ( readback ) gerekliliği sorgulandı mı?
- Köprü mimarisinde tek kaynaklı doku / tampon politikası var mı?