holodepth

HTML5 Canvas · Performans ve optimizasyon

Yeniden çizim maliyeti: tuvalde piksel başına ödenen fiyat

Her drawImage, dolgu veya gölgeli yol, tarayıcıda yeniden rasterize / birleştirme işi tetikler; tuval ne kadar büyük ve işlemler ne kadar pahalıysa kare süresi o kadar şişer. Bu sayfa «ne sıklıkla tüm ekranı silip baştan çizmeliyim?» ve «hangi bölgeleri gerçekten güncelledim?» sorularını Canvas 2D bakış açısıyla sabitler — tam clearRect ile dirty rectangle ayrımı, katman tuval stratejisi ve bağlam özelliklerinin maliyet ipuçlarını içerir. Zihinsel çerçeve Update vs render ve Temizle ve yeniden çiz sayfalarıyla birlikte okunmalıdır.

Çağrı birleştirme ve toplu çizim Batching mantığı başlığında; boyutlandırma ve piksel oranı Yeniden boyutlandırma mantığı ile ilişkilidir.

Özet: maliyet kaynakları

Kaynak Etki Azaltma
Tuval alanı Silme ve dolgu piksel sayısı Kısmi temizlik / katman
Üst üste çizim Aynı piksel tekrar işlenir Sıra ve opaklık disiplini
Gölge, süzgeç Ek geçişler Sınırlı kullanım, önbake
save / restore Yığın maliyeti Dar kapsam
Gereksiz kare Sabit sahne yine çizilir Kirli / bayrak

Tuval yeniden çizimi ve piksel bütçesi

Canvas 2D’de «bir kare» çoğu zaman tüm hedef yüzeyin güncellenmesi anlamına gelmez; pratikte ise basit oyunlar her turda clearRect(0,0,w,h) ile başlayıp her şeyi yeniden çizmeyi seçer. Bu, kodu sadeleştirir ama piksel sayısı ( genişlik × yükseklik × cihaz piksel oranı ) ile doğru orantılı bir maliyet doğurur — sahne karmaşık değilken bile çözünürlük büyüdükçe süre büyür. Tamponu baştan sona silmek, aynı çözünürlükte her piksele en az bir yazma (ve genelde ikinci bir tam çizim turu) demektir; ölçümde canvas.width / height (cihaz pikselleri) doğrudan bütçe alanıdır.

«Yeniden çizim maliyeti» hem kaç kez çizdiğiniz hem de ne kadar alanı etkilediğiniz ile belirlenir. İnce taneli parçacık, ışık saçan çizgiler aynı piksele defalarca yazılabilir; her geçiş birleştirme kurallarına göre ek iş yükü taşır. Bu «üst üste boyama» ( overdraw ) zengin görünen ama nesne sayısı düşük sahnelerde bile süreyi şişirebilir. Bu yüzden üretimde profil ( tarayıcı performans paneli: ana iş parçacığı, boyama / raster adımları ) ile gerçek darboğazı ölçmeden optimizasyon sırası seçilmemelidir.

Kare yönetimi ile uyum: bütçeyi kare başına milisaniye cinsinden düşünmek, redraw stratejisini ürün kararı haline getirir — özellikle mobilde. Örneğin 60 kare / saniye hedefinde yaklaşık 16,7 ms içinde fizik, girdi ve oyun mantığı da pay alır; çizime kalan pay yazılı tanımlandığında hangi optimizasyonun erken, hangisinin zorunlu olduğu netleşir.

Tam ekran temizlik–yeniden çizim varsayılanı çoğu prototip için yeterlidir; kare süresi bütçeyi aştığında sırayla kirli dikdörtgen, katman, gereksiz kare atlama ve DPR ayarı ( 6. bölüm ) değerlendirilir.

Tam temizlik ve kirli dikdörtgen stratejisi

clearRect hedeflenen bölgeyi şeffaf yapar; tüm tuval yerine yalnız hareket eden sprite çevresini temizleyip yeniden çizmek piksel sayısını düşürür. Zorluk, kirli bölgenin sınırlarını doğru birleştirmek ve arka plan ( paralaks katman, dünya döşemesi ) değiştiyse yine genişletmek gerektiğinde ortaya çıkar — yanlış küçültme iz bırakan parçalar ( görüntü hayaletleri ) üretir. Tipik akış: her değişen öğe için bir dikdörtgen çıkar → kare boyunca mergeDirtyRect ile birleşim kutusunu büyüt → yalnız o kutuda clearRect ve yeniden çizim. Arka plan statik katmanda tutuluyorsa üst tuvaldeki kirli alan genellikle daha küçük kalır.

Basit sahnelerde kirli dikdörtgen birleştirme ( birleşim kutusu ) ile yönetilir; parçacık sistemi ve çoklu aktörlerde kutu hızla tüm ekrana yaklaşır — bu durumda tam temizlik daha uygun olabilir. Strateji sahneye göre seçilir; evrensel bir doğru yoktur. Kararı verirken «bir karede kaç aktör kutuyu kirletiyor?» sorusunu piksel bütçesi ile birlikte yanıtlamak, hayal kırıklığını azaltır.

clip ile çizimi kısıtlamak da bir seçenektir; yanlış kullanımda hata ayıklaması zorlaşır — kırpma yolu da save / restore yığınına girer; kaybolan çizgiler çoğu zaman «tahta dışında kaldım» sanısıyla aslında kırpılmış bölgededir. Bağlam sırası 2D bağlam düşüncesiyle birlikte ele alınmalıdır.

/** İlk kirli alan veya genişletilmiş birleşim kutusu (mantık koordinatlarında). */
function mergeDirtyRect(current, x, y, w, h) {
  if (w <= 0 || h <= 0) return current;
  if (!current) return { x, y, w, h };
  const x2 = Math.max(current.x + current.w, x + w);
  const y2 = Math.max(current.y + current.h, y + h);
  const nx = Math.min(current.x, x);
  const ny = Math.min(current.y, y);
  return { x: nx, y: ny, w: x2 - nx, h: y2 - ny };
}

Katman tuval: statik ve dinamik ayırımı

Arka plan ve dünya ızgarası her karede aynıysa bir «arka tuval» ( hafızada ikinci canvas ) üzerinde bir kez veya nadiren çizip ana tuvalde drawImage(staticLayer, ...) ile yapıştırmak CPU/GPU-maliyetini düşürür; üstte hareketli varlıklar küçük kirli bölgelerde güncellenir. Bellek karşılığı: ek piksel tamponu. Statik katmanın yeniden çizimi genelde seyrek olaylara bağlanır: ilk kurulum, varlık yükü, yeniden boyut veya bölüm / seviye değişimi; aradaki karelerde yalnız tek bir drawImage ve üstteki kısmi yenileme maliyeti ödenir.

Ara katmanlar çoğaldıkça bellek ve senkronizasyon ( her yeniden boyutta üç katmanı da yeniden ölçeklemek ) maliyeti artar — bu yüzden iki veya üç anlamlı katman ( statik / paralaks / dinamik ) ile başlamak pratik üst sınır gibidir. Uygulama boyutuna göre gözden geçirilir.

Offscreen tuval kullanımı ileride Offscreen Canvas sayfasına taşınır; burada yalnız maliyet gerekçesi işaretlenir.

Bağlam durumu ve pahalı özellikler

shadowBlur, filter, globalCompositeOperation gibi özellikler caziplidir; açık kaldıkları her primitif için ek iş yükü doğurabilir. Çizim yardımcınız, geçici olarak açıp kapattığı bu ayarları yerel bir save / restore bloğuna almalı; üretimde «kurulumu unuttum» halleri kare süresinde görünür. Sürekli stil sıçramasını azaltmak için pahalı modları az çağrıda toplama batching konusunda işlenir; burada odak, hangi ayarların açık unutulduğunda ek faturalandığıdır. Benzer üretim tuzakları 8. bölümde madde madde listelenir.

save / restore yığını derinleştikçe maliyet artar; mümkün olduğunca sığ tutun veya explicit atamalarla eski değerlere dönün. Özellikle döngü içinde her varlıkta save üst üste binmesi profilde fark edilir; dönüşümleri geri alma disiplini temizle ve çiz akışıyla çelişmeden yürütülmelidir; yığın derinliğinin kendisi için bkz. 8.

Metin çizimi ve yol sıra doldurma ( stroke/fill ) karmaşık harf formlarında pahalıdır; her karede binlerce dinamik metin etiketi çizmek yerine nadir güncellenen etiketleri önbelleğe almak ( küçük ara tuval veya düşük frekanslı katman ) yaygın profesyonel çözümdür — detay ürün kararıdır; ileri varyantlar Offscreen Canvas ile üretim düzenine taşınır.

Ağır filter zinciri veya büyük shadowBlur rasterı genişletir; maliyet yalnız «özellik açık mı?» değil, etkilenen dikdörtgenin boyutu ve hedef çözünürlük ile de büyür. Mobil profilde düşük kare genelde önce burada belirginleşir ( DPR ile çarpılır ).

Gereksiz kare çizimi: kirli bayrak ve atlama

Durum değişmediyse tüm sahneyi yeniden çizmek piksel bütçesini boşa harcar; basit desen: güncelleme aşamasında değişiklik olduğunda needsRedraw = true yapılır, çizim döngüsü başında kontrol edilir. requestAnimationFrame döngüsü yine çalışabilir; fark, çizim dalının erken dönmesi ve gereksiz clearRect / draw çağrılarının yapılmamasıdır. Sekme arka plandayken veya duraklatıldığında çizimi atlamak pil ve ısıyı düşürür — duraklatma politikası ile uyumlanır. Bu düzen, piksel bütçesi ile aynı dilde konuşur: değişmeyen karede maliyet sıfıra yakınsın.

Yalnız kısmi kirli stratejisi kullanılıyorsa, değişmeyen karelerde bile birleşim kutusu tüm ekrana genişleyebilir — bu durumda bayrak «beni yine de çiz» olarak kalır; telemetri ile gerçekten kazanım olup olmadığını ölçün. Kutunun şişmesi 2. bölümdeki birleştirme mantığından kaynaklanıyorsa, tam ekrana dönmek bazen daha az sürprizlidir.

Bazı tarayıcılarda kullanıcı etkileşimi olmayan sekmeler kare hızını düşürür; animasyon yine de düşünüldüğünde bu, iç mantık güncellemesi ile çizim atlama kararını etkiler. Sekme görünmez kare hızı düşünce «her şey dondu» sanısı doğabilir; simülasyon gerçek zamanlı kalmalıysa bu davranış ürün / test planında açıkça ele alınmalıdır.

function requestRedraw(state) {
  state.needsRedraw = true;
}

function consumeRedraw(state) {
  const v = state.needsRedraw;
  state.needsRedraw = false;
  return Boolean(v);
}

Cihaz piksel oranı ve iç çözünürlük

CSS genişliği 800 px, devicePixelRatio = 2 ise arka tampon genelde 1600 piksel genişlikte çizilir; her clearRect ile silinen alan iki katına çıkmış olur. Keskin görüntü için ölçekleme gerekir; performans baskısında hedef oranı düşürmek ( 1,5 veya 1 ) bilinçli bir piksel azaltımıdır — bulanıklık ile hız takas edilir.

Mantık dünyası koordinatları ile tuval aygıt pikselleri arasında tutarlı dönüşüm tek yerde tanımlanmalıdır; kirli dikdörtgen hesapları yanlış ölçeklenirse yine hayalet artefaktları oluşur.

Pencere taşınması, tam ekran geçişi gibi olaylar yeniden boyutlandırma ile tetiklenir; ölçek yeniden kurulmadan devam eden redraw yanlış görüntü üretir.

Boyut değişimi ve katman senkronu

Tuval genişliği değiştiğinde hem ana yüzey hem varsa ara tamponlar aynı mantıkla güncellenmezse yeniden çizim «doğru kod ama yanlış çözünürlük» üretir. Yeniden boyutlandırma mantığı sayfası bu geçişi yönetir; performans sayfası yalnız maliyet uyarısı verir: her boyut değişiminde tüm katmanların içeriklerinin geçerli olması gerekir.

Büyük çözünürlük sıçramalarında anında tüm görselleri yeniden ölçeklemek takılmaya yol açabilir; bir karelik degradasyon veya ilerleyen yeniden çizim politikası ürün tercihidir.

Ağır yeniden ölçekleme işlemi çizim döngüsünden çıkarılıp arka planda veya düşük öncelikli kareye yaymak ana iş parçacığını rahatlatır — karmaşık projelerde işçi tuval düşünülür.

Anti-kalıplar: aşırı kare ve unutulan durum

Aşağıdaki alışkanlıklar profil olmadan «neden düşük FPS?» sorusunu zorlaştırır; tarayıcı performans panelinde uygulama veya raster iş parçacığı süresi ani sıçramalar gösterir. Bu sayfanın ölçütü piksel ve bağlam başına maliyettir — taban kavram birinci bölümde özetlenmiştir.

Her öğede bağımsız gölge / süzgeç: Her sprite için shadowBlur veya ağır filter açmak, primitif sayısıyla çarpan ek geçiş üretir; mobil GPU tarafında darboğaz sık görülür. Çözüm: sınırlı «pahalı mod» geçişi, önceden gölgelenmiş doku kullanımı veya çağrıları gruplamak ( batching ). Pahalı ayarların kapsamı dördüncü bölüm ile uyumlu dar tutulmalıdır.

Kirli bölgeyi küçük hesaplayıp kısmi temizlik: Birleşim kutusu hareketli nesneyi tam kapsamazsa şeffaf veya eski piksel «hayalet» olarak kalır; bazen tek kare, bazen birikimli iz. Ya kutuyu güvenli biçimde genişletin ya da birleştirme mantığını mergeDirtyRect ile gözden geçirin; karmaşık sahnede tam silip baştan çizmek ( piksel bütçesi ) bazen daha ucuz olur.

Her varlıkta derin save yığını: İç içe save / restore doğru olsa bile CPU tarafında maliyet ve hata riski artar; dönüşümleri ters sırayla geri almak veya daha sığ sarmalayıcılar kullanmak profili düzleştirir. Çizim sırasını ürün seviyesinde planlamak, temizle ve çiz disipliniyle çakışmadan yürür.

Yüksek DPR ile her kare tam silme: İç tampon alanı cssWidth * dpr ile büyür; her karede clearRect(0,0,w,h) piksel sayısını doğrudan çarpar. Hedef oranı düşürmek veya statik katmanda tam silmeyi azaltmak ( katman stratejisi ) tipik rahatlamadır — ayrıntı altıncı bölümde. Gereksiz tam redraw’ları yeniden çizim bayrağı ile kesmek ayrı kazançtır.

Bu sayfanın sınırı

Donanıma özel GPU komut kuyruğu analizi ve WebGPU burada işlenmez. Odak: Canvas 2D yeniden çiziminin sezgisel piksel ve durum maliyeti ile ürün içi strateji seçimidir.

  • Tam temizlik piksel bütçesiyle gerekçelendirildi mi?
  • Kirli birleşim kutusu doğru genişliyor mu?
  • Pahalı bağlam ayarları dar kapsamda mı?
  • Gereksiz kareler needsRedraw ile eleniyor mu?
  • DPR ve yeniden boyut senkronu katmanlara uygulandı mı?