MSAA antialiasing yazısı, örnek sayısı başına bir render pipeline önceden oluşturarak WebGPU çizgi grafiğine multi-sample anti-aliasing eklemeyi gösterdi. Ama hangi örnek sayılarını gerçekten kullanabilirsin? WebGPU spec’i yalnızca 1× ve 4בü garanti eder — 2×, 8× ve 16× adaptöre bağlıdır. Desteklenmeyen bir örnek sayısıyla pipeline oluşturursan, hata vermeden derlenebilir ama görünür çıktı üretmeyebilir.
Bu yazı, her aday örnek sayısını test eden ve sonucu HTML listesi olarak
render eden minimal, bağımlılıksız bir probe gösterir. Aşağıdaki canlı
demo, /scripts/msaa-levels-webgpu.js
dosyasından birebir aynı script’i yükler.
Yukarıdaki liste her örnek sayısını createRenderPipelineAsync
ile sorgulayarak üretilir. WebGPU destekli bir tarayıcı gerekir
(Chrome / Edge 113+).
Nasıl çalışır
Probe basittir: her aday örnek sayısı (1, 2, 4, 8, 16) için, o
multisample.count ile createRenderPipelineAsync kullanarak bir
render pipeline oluşturmayı dene. Promise resolve olursa örnek sayısı
destekleniyor, reject olursa desteklenmiyor.
for (const sc of SAMPLE_COUNTS) {
try {
await device.createRenderPipelineAsync({
layout: 'auto',
vertex: { module, entryPoint: 'vs_main' },
fragment: { module, entryPoint: 'fs_main', targets: [{ format }] },
primitive: { topology: 'triangle-list' },
multisample: { count: sc },
});
supported.push(sc);
} catch {
// Desteklenmeyen örnek sayısı — atla.
}
}Probe için shader içeriği önemli değil — yalnızca örnek sayısının kabul edilip edilmediğini test eden geçerli bir render pipeline’a ihtiyacımız var. En basit pipeline (solid-color fragment shader’lı tek bir tam-ekran üçgeni) yeterli.
Neden createRenderPipelineAsync?
Senkron createRenderPipeline, validation hatalarında throw etmez —
geçersiz bir pipeline nesnesi döndürür ve konsola log’lar. Bu probe için
güvenilmez kılar: konsolu incelemen veya error-scope API’leri kullanman
gerekir. createRenderPipelineAsync ise validation hatalarında
reject olan bir Promise döndürür, bu da probe için en taşınabilir
yoldur. pushErrorFilter/popErrorFilter gibi henüz yaygın olarak
gönderilmeyen yeni API’lerin aksine WebGPU’nun çıkışından beri mevcuttur.
JavaScript’ten sonuca erişim
Script desteklenen örnek sayılarını üç şekilde sunar:
- HTML listesi — desteklenen sayı başına bir
<li>içeren<ul>, her biridata-sample-count="N"ile etiketli. Tek tek girişleri CSS selector’ları veyaquerySelectorAllile sorgulayabilirsin. - Custom event — div üzerinde bir
msaalevelsevent’i dispatch edilir,event.detail.levelsdesteklenen sayıların dizisini içerir. Global callback olmadan çalışır:example.jsdocument.querySelector('.msaa-levels') .addEventListener('msaalevels', (e) => { console.log('Supported:', e.detail.levels); }); - Callback — div’de
data-onlevels="functionName"attribute’u varsa,window[functionName](supported)diziyle çağrılır. Bu, direction-cubes yazısında kullanılan callback deseniyle aynıdır.
Kendi sayfanda kullanma
/scripts/msaa-levels-webgpu.js dosyasını statik klasörüne koy ve
msaa-levels sınıflı bir div ekle:
<div class="msaa-levels" data-onlevels="onMsaaLevels"></div>
<script>
function onMsaaLevels(levels) {
console.log('Supported MSAA sample counts:', levels);
}
</script>
<script src="/scripts/msaa-levels-webgpu.js" defer></script>data-onlevels attribute’u opsiyoneldir — varsa, adı verilen fonksiyon
desteklenen örnek sayılarının dizisiyle çağrılır. Yoksa yine de
msaalevels custom event’ini dinleyebilir veya render edilmiş HTML
listesini okuyabilirsin.
Tam kaynak
// SPDX-FileCopyrightText: 2026 Uli Köhler <gitlab@techoverflow.net>
// SPDX-License-Identifier: CC0-1.0
//
// Minimal WebGPU MSAA desteklenen-örnek-sayısı probe'u.
//
// Mevcut GPU adaptörünün hangi MSAA örnek sayılarını (1, 2, 4, 8, 16)
// desteklediğini sorgular ve sonucu sayfadaki her
// <div class="msaa-levels"> elemanı içinde HTML <ul> listesi olarak render eder.
//
// Liste JavaScript'ten şu yollarla erişilebilir:
// 1. data-onlevels="functionName" ile belirtilen callback —
// desteklenen örnek sayılarının dizisiyle çağrılır (ör. [1, 2, 4, 8]).
// 2. div üzerinde dispatch edilen custom 'msaalevels' event'i,
// event.detail.levels diziyi içerir.
//
// Bu, interactive-msaa-antialiasing-for-webgpu-line-plots yazısının
// kullandığı probe tekniğiyle aynıdır: her aday örnek sayısı için
// createRenderPipelineAsync ile bir render pipeline oluştur. Promise
// reject olursa, örnek sayısı bu adaptörde desteklenmiyor demektir.
//
// Neden IIFE? Her şeyi hemen çağrılan fonksiyon ifadesinde sarıyoruz
// ki helper'lar, sabitler veya WGSL kaynağı global scope'a sızmasın.
// Script'in <script src=... defer> ile birebir include edilmesi,
// muhtemelen aynı sayfadaki diğer script'lerle birlikte, amaçlanmıştır;
// bu yüzden izolasyon önemlidir.
(function () {
'use strict';
// ─────────────────────────────────────────────────────────────────────
// Configuration
// ─────────────────────────────────────────────────────────────────────
// WebGPU spec'i yalnızca 1× ve 4×'ü garanti eder; 2×, 8× ve 16×
// adaptöre bağlıdır. Beşinin hepsini sorgulayıp hangilerinin
// başarılı olduğunu raporlarız. Render edilen liste doğal okunsun
// diye sıralama artandır.
const SAMPLE_COUNTS = [1, 2, 4, 8, 16];
// ─────────────────────────────────────────────────────────────────────
// Probe için minimal WGSL shader
// ─────────────────────────────────────────────────────────────────────
// Probe için shader içeriği önemli değil — yalnızca örnek sayısının
// kabul edilip edilmediğini test eden geçerli bir render pipeline'a
// ihtiyacımız var. Bu, olası en basit render pipeline'dır: solid-color
// fragment shader'lı tek bir üçgen. Asla render edilmez; yalnızca
// createRenderPipelineAsync'in başarılı mı reject mi olduğu önemli.
const SHADER = /* wgsl */ `
@vertex
fn vs_main(@builtin(vertex_index) vi : u32) -> @builtin(position) vec4<f32> {
// Tam ekranı kaplayan tek üçgen — render edilmiyor, sadece
// geçerli bir vertex shader olması gerekiyor.
let pos = array<vec2<f32>, 3>(
vec2<f32>(-1.0, -1.0),
vec2<f32>( 3.0, -1.0),
vec2<f32>(-1.0, 3.0),
);
return vec4<f32>(pos[vi], 0.0, 1.0);
}
@fragment
fn fs_main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 1.0, 1.0, 1.0);
}
`;
// ─────────────────────────────────────────────────────────────────────
// Desteklenen örnek sayılarını sorgula
// ─────────────────────────────────────────────────────────────────────
// Her aday örnek sayısı için, o sampleCount ile bir render pipeline
// oluşturmayı dene. createRenderPipelineAsync validation hatalarında
// (desteklenmeyen örnek sayıları dahil) reject olan bir Promise
// döndürür. Bu probe için en taşınabilir yoldur — pushErrorFilter/
// popErrorFilter gibi henüz yaygın gönderilmeyen yeni API'lerin
// aksine WebGPU'nun çıkışından beri mevcuttur.
//
// Probe pipeline'ları layout: 'auto' ve basit bir shader ile
// oluşturulur — kabul testi dışında bir şey için gerekli değiller.
// GPU belleğini serbest bırakmak için sorgulama sonrası hemen
// destroy edilirler.
//
// Döndürür: desteklenen örnek sayılarının (artan) dizisine resolve
// olan bir Promise, ör. [1, 4] veya [1, 2, 4, 8].
async function probeSampleCounts(device, format) {
const module = device.createShaderModule({ code: SHADER });
const supported = [];
for (const sc of SAMPLE_COUNTS) {
let pipeline = null;
try {
pipeline = await device.createRenderPipelineAsync({
layout: 'auto',
vertex: { module, entryPoint: 'vs_main' },
fragment: { module, entryPoint: 'fs_main', targets: [{ format }] },
primitive: { topology: 'triangle-list' },
multisample: { count: sc },
});
supported.push(sc);
} catch {
// Desteklenmeyen örnek sayısı — sessizce atla.
} finally {
// Pipeline nesnesi yalnızca probe içindir; GPU belleğini serbest
// bırakmak için destroy et. createRenderPipelineAsync destroy()
// metodu olmayan bir GPURenderPipeline döndürür, ancak referans
// düştüğünde GPU kaynakları garbage-collect edilir.
// GC'ye yardım için referansı null yapıyoruz.
pipeline = null;
}
}
return supported;
}
// ─────────────────────────────────────────────────────────────────────
// Desteklenen seviyeler listesini div'e render et
// ─────────────────────────────────────────────────────────────────────
// Desteklenen örnek sayısı başına bir <li> içeren HTML <ul> oluşturur.
// Her <li> sayıyı "N×" olarak gösterir ve JS'in tek tek girişleri
// sorgulayabilmesi için data-sample-count="N" ile etiketlenir.
//
// Div ayrıca şunları alır:
// - detail.levels = desteklenen dizi içeren custom 'msaalevels' event'i
// - data-onlevels="functionName" üzerinden callback (belirtildiyse)
function renderList(div, supported) {
// <ul> listesini oluştur.
const ul = document.createElement('ul');
ul.className = 'msaa-levels-list';
for (const sc of supported) {
const li = document.createElement('li');
li.textContent = sc + '\u00d7'; // × karakterini kullanan "N×"
li.setAttribute('data-sample-count', sc);
ul.appendChild(li);
}
// Div'in içeriğini listeyle değiştir.
div.innerHTML = '';
div.appendChild(ul);
// Harici JS'in global callback gerekmeden probe sonucuna
// tepki verebilmesi için custom event dispatch et.
div.dispatchEvent(new CustomEvent('msaalevels', {
bubbles: true,
detail: { levels: supported },
}));
// Opsiyonel data-onlevels callback'ini çağır.
const cbName = div.getAttribute('data-onlevels');
if (cbName && typeof window[cbName] === 'function') {
window[cbName](supported);
}
}
// ─────────────────────────────────────────────────────────────────────
// Div başına başlatma
// ─────────────────────────────────────────────────────────────────────
// Sayfadaki her <div class="msaa-levels"> için bir kez çağrılır.
// WebGPU adapter+device ister, tüm aday örnek sayılarını sorgular
// ve sonucu HTML listesi olarak render eder.
async function initDiv(div) {
// Sorgulama sırasında yükleme placeholder'ı göster.
div.textContent = 'Probing MSAA support\u2026';
if (!navigator.gpu) {
div.innerHTML = '<p>WebGPU is not supported in this browser.</p>';
div.dispatchEvent(new CustomEvent('msaalevels', {
bubbles: true,
detail: { levels: [], error: 'no-webgpu' },
}));
return;
}
const adapter = await navigator.gpu.requestAdapter();
if (!adapter) {
div.innerHTML = '<p>No WebGPU adapter available.</p>';
div.dispatchEvent(new CustomEvent('msaalevels', {
bubbles: true,
detail: { levels: [], error: 'no-adapter' },
}));
return;
}
const device = await adapter.requestDevice();
const format = navigator.gpu.getPreferredCanvasFormat();
// Tüm aday örnek sayılarını sorgula.
const supported = await probeSampleCounts(device, format);
// Device ile işimiz bitti — serbest bırak. Probe pipeline'ları
// zaten garbage-collect edilebilir (referans tutulmuyor).
device.destroy();
// Listeyi render et ve listener'lara bildir.
renderList(div, supported);
}
// ─────────────────────────────────────────────────────────────────────
// Otomatik başlatma
// ─────────────────────────────────────────────────────────────────────
// Sayfadaki her <div class="msaa-levels"> elemanını bul ve başlat.
// Doküman hâlâ yükleniyorsa (script <head>'den veya defer ile
// çalıştıysa), div'lerin var olması için DOMContentLoaded'ı bekle.
// Doküman zaten hazırsa (script geç eklendiyse), hemen başlat.
function initAll() {
document.querySelectorAll('div.msaa-levels').forEach(initDiv);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initAll);
} else {
initAll();
}
})();Script ve yukarıdaki inline listeleme aynıdır ve ikisi de
CC0-1.0 ile yayımlanmıştır. Sorgulama tekniği, desteklenen örnek
sayıları için render pipeline’larını önceden oluşturan
MSAA antialiasing yazısının
ve MSAA ile MSAA’sız render yolları arasında seçim yapan
WebGCodeViewer projesindeki
ToolpathRenderer‘ın kullandığıyla aynıdır.