WebGPU'yu kullanmaya başlama

Jetpack WebGPU'yu kullanmak için projenizin aşağıdaki minimum koşulları karşılaması gerekir:

  • Minimum API düzeyi: Android API 24 (Nougat) veya sonraki sürümler gereklidir.
  • Donanım: Arka uç için Vulkan 1.1 veya sonraki sürümleri destekleyen cihazlar tercih edilir.
  • Uyumluluk modu ve OpenGL ES desteği: GPUAdapter istenirken standartlaştırılmış featureLevel seçeneği compatibility olarak ayarlanarak WebGPU'nun uyumluluk modu ile kullanılması mümkündür.
// Example of requesting an adapter with "compatibility" mode enabled:
val adapter = instance.requestAdapter(
  GPURequestAdapterOptions(featureLevel = FeatureLevel.Compatibility))

Yükleme ve kurulum

Ön koşullar:

Android Studio: Android Studio'nun en son sürümünü resmi web sitesinden indirin ve Android Studio Yükleme Kılavuzu'ndaki talimatları uygulayın.

Yeni proje oluşturma

Android Studio yüklendikten sonra WebGPU projenizi ayarlamak için aşağıdaki adımları uygulayın:

  1. Yeni bir proje başlatma: Android Studio'yu açın ve Yeni Proje'yi tıklayın.
  2. Şablon seçin: Android Studio'da Boş Etkinlik şablonunu seçip İleri'yi tıklayın.

    Android Studio'nun sizin adınıza oluşturacağı etkinliklerin yerleşik listesini gösteren Android Studio Yeni Proje iletişim kutusu.
    Şekil 1. Android Studio'da yeni bir proje oluşturma
  3. Projenizi yapılandırın:

    • Ad: Projenize bir ad verin (ör. "JetpackWebGPUSample").
    • Paket Adı: Paket adının, seçtiğiniz ad alanıyla (ör. com.example.webgpuapp) eşleştiğini doğrulayın.
    • Dil: Kotlin'i seçin.
    • Minimum SDK: Bu kitaplık için önerilen API 24: Android 7.0 (Nougat) veya sonraki bir sürümü seçin.
    • Build Configuration Language: Modern bağımlılık yönetimi için Kotlin DSL (build.gradle.kts) kullanılması önerilir.
    Yeni boş etkinliği doldurmak için alanlar içeren Android Studio Boş Etkinlik iletişim kutusu (ör. Ad, Paket Adı, Kaydetme Konumu ve Minimum SDK).
    Şekil 2. Boş bir etkinlikle başlama
  4. Bitir: Bitir'i tıklayın ve Android Studio'nun proje dosyalarınızı senkronize etmesini bekleyin.

WebGPU Jetpack kitaplığını ekleme

androidx.webgpu kitaplığı, WebGPU NDK .so kitaplık dosyalarının yanı sıra yönetilen kod arayüzlerini içerir.

Kitaplık sürümünü güncellemek için build.gradle dosyanızı güncelleyebilir ve Android Studio'daki "Projeyi Senkronize Et" düğmesini kullanarak projenizi Gradle dosyalarıyla senkronize edebilirsiniz.

Üst düzey mimari

Android uygulamasında WebGPU oluşturma işlemi, kullanıcı arayüzünün yanıt verme özelliğini korumak için özel bir oluşturma iş parçacığında çalıştırılır.

  • Kullanıcı arayüzü katmanı: Kullanıcı arayüzü, Jetpack Compose ile oluşturulur. WebGPU çizim yüzeyi, AndroidExternalSurface kullanılarak Compose hiyerarşisine entegre edilir.
  • Oluşturma Mantığı: Özel bir sınıf (ör. WebGpuRenderer), tüm WebGPU nesnelerini yönetmekten ve oluşturma döngüsünü koordine etmekten sorumludur.
  • Gölgeleyici Katmanı: res veya dize sabitlerinde depolanan WGSL gölgeleyici kodu.
Bir WebGPU Android uygulamasında kullanıcı arayüzü iş parçacığı, özel bir oluşturma iş parçacığı ve GPU donanımı arasındaki etkileşimi gösteren üst düzey mimari şeması.
Şekil 3. Android'de WebGPU'nun üst düzey mimarisi

Adım adım: örnek uygulama

Bu bölümde, temel WebGPU iş akışını göstererek ekranda renkli bir üçgen oluşturmak için gereken temel adımlar açıklanmaktadır.

Ana Etkinlik

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            WebGpuSurface()
        }
    }
}

The external surface Composable

WebgpuSurface.kt adlı yeni bir dosya oluşturun. Bu Composable, Compose ile oluşturucunuz arasında köprü oluşturmak için AndroidExternalSurface öğesini sarmalar.

@Composable
fun WebGpuSurface(modifier: Modifier = Modifier) {
    // Create and remember a WebGpuRenderer instance.
    val renderer = remember { WebGpuRenderer() }
    AndroidExternalSurface(
        modifier = modifier.fillMaxSize(),
    ) {
        // This block is called when the surface is created or resized.
        onSurface { surface, width, height ->
            // Run the rendering logic on a background thread.
            withContext(Dispatchers.Default) {
                try {
                    // Initialize the renderer with the surface
                    renderer.init(surface, width, height)
                    // Render a frame.
                    renderer.render() 
                } finally {
                    // Clean up resources when the surface is destroyed.
                    renderer.cleanup()
                }
            }
        }
    }
}

Oluşturucuyu ayarlama

WebGpuRenderer.kt içinde WebGpuRenderer sınıfı oluşturun. Bu sınıf, GPU ile iletişim kurma işini üstlenir.

Öncelikle sınıf yapısını ve değişkenleri tanımlayın:

class WebGpuRenderer() {
    private lateinit var webGpu: WebGpu
    private lateinit var renderPipeline: GPURenderPipeline
}

Başlatma: Ardından, WebGPU örneğini oluşturmak ve yüzeyi yapılandırmak için init işlevini uygulayın. Bu işlev, daha önce oluşturduğumuz harici yüzey composable'ının içindeki AndroidExternalSurface kapsamı tarafından çağrılır.

Not: init işlevi, kurulumu kolaylaştırmak için yardımcı bir yöntem olan createWebGpu'i (androidx.webgpu.helper'nin bir parçası) kullanır. Bu yardımcı program, WebGPU örneğini oluşturur, bir bağdaştırıcı seçer ve cihaz isteğinde bulunur.

// Inside WebGpuRenderer class
suspend fun init(surface: Surface, width: Int, height: Int) {
    // 1. Create Instance & Device
    webGpu = createWebGpu(surface)
    val device = webGpu.device

    // 2. Setup Pipeline (compile shaders)
    initPipeline(device)

    // 3. Configure the Surface
    webGpu.webgpuSurface.configure(
      GPUSurfaceConfiguration(
        device,
        width,
        height,
        TextureFormat.RGBA8Unorm,
      )
    )
  }

androidx.webgpu kitaplığı, derleme sistemi tarafından otomatik olarak bağlanan ve yönetilen JNI ve .so dosyalarını içerir. Yardımcı yöntem createWebGpu, paketlenmiş libwebgpu_c_bundled.so öğesinin yüklenmesini sağlar.

Ardışık düzen kurulumu

Artık bir cihazımız olduğuna göre GPU'ya üçgenimizi nasıl çizeceğini söylememiz gerekiyor. Bunu, gölgelendirici kodumuzu (WGSL ile yazılmış) içeren bir "ardışık düzen" oluşturarak yaparız.

Gölgelendiricileri derlemek ve oluşturma işlem hattını oluşturmak için bu özel yardımcı işlevi WebGpuRenderer sınıfınıza ekleyin.

// Inside WebGpuRenderer class
private fun initPipeline(device: GPUDevice) {
    val shaderCode = """
        @vertex fn vs_main(@builtin(vertex_index) vertexIndex : u32) ->
        @builtin(position) vec4f {
            const pos = array(vec2f(0.0, 0.5), vec2f(-0.5, -0.5), vec2f(0.5, -0.5));
            return vec4f(pos[vertexIndex], 0, 1);
        }
        @fragment fn fs_main() -> @location(0) vec4f {
            return vec4f(1, 0, 0, 1);
        }
    """

    // Create Shader Module
    val shaderModule = device.createShaderModule(
      GPUShaderModuleDescriptor(shaderSourceWGSL = GPUShaderSourceWGSL(shaderCode))
    )

    // Create Render Pipeline
    renderPipeline = device.createRenderPipeline(
      GPURenderPipelineDescriptor(
        vertex = GPUVertexState(
          shaderModule,
        ), fragment = GPUFragmentState(
          shaderModule, targets = arrayOf(GPUColorTargetState(TextureFormat.RGBA8Unorm))
        ), primitive = GPUPrimitiveState(PrimitiveTopology.TriangleList)
      )
    )
  }

Çerçeve çizme

İşlem hattı hazır olduğunda oluşturma işlevini uygulayabiliriz. Bu işlev, ekrandan bir sonraki kullanılabilir dokuyu alır, çizim komutlarını kaydeder ve bunları GPU'ya gönderir.

Bu yöntemi WebGpuRenderer sınıfınıza ekleyin:

// Inside WebGpuRenderer class
fun render() {
    if (!::webGpu.isInitialized) {
      return
    }

    val gpu = webGpu

    // 1. Get the next available texture from the screen
    val surfaceTexture = gpu.webgpuSurface.getCurrentTexture()

    // 2. Create a command encoder
    val commandEncoder = gpu.device.createCommandEncoder()

    // 3. Begin a render pass (clearing the screen to blue)
    val renderPass = commandEncoder.beginRenderPass(
      GPURenderPassDescriptor(
        colorAttachments = arrayOf(
          GPURenderPassColorAttachment(
            GPUColor(0.0, 0.0, 0.5, 1.0),
            surfaceTexture.texture.createView(),
            loadOp = LoadOp.Clear,
            storeOp = StoreOp.Store,
          )
        )
      )
    )

    // 4. Draw
    renderPass.setPipeline(renderPipeline)
    renderPass.draw(3) // Draw 3 vertices
    renderPass.end()

    // 5. Submit and Present
    gpu.device.queue.submit(arrayOf(commandEncoder.finish()))
    gpu.webgpuSurface.present()
  }

Kaynak temizleme

Yüzey yok edildiğinde WebGpuSurface tarafından çağrılan temizleme işlevini uygulayın.

// Inside WebGpuRenderer class
fun cleanup() {
    if (::webGpu.isInitialized) {
      webGpu.close()
    }
  }

Oluşturulan çıkış

Bir Android telefon ekranının görüntüsünde, WebGPU uygulamasının çıktısı gösteriliyor: koyu mavi arka plan üzerinde ortalanmış, kırmızı renkli bir üçgen.
Şekil 4. Kırmızı üçgen gösteren örnek WebGPU uygulamasının oluşturulmuş çıkışı

Örnek uygulama yapısı

Örnek uygulamada kullanılan yapıda olduğu gibi, oluşturma uygulamanızı kullanıcı arayüzü mantığınızdan ayırmak iyi bir uygulamadır:

app/src/main/
├── java/com/example/app/
│   ├── MainActivity.kt       // Entry point
│   ├── WebGpuSurface.kt      // Composable Surface
│   └── WebGpuRenderer.kt     // Pure WebGPU logic
  • MainActivity.kt: Uygulama giriş noktası. İçeriği WebGpuSurface Composable olarak ayarlar.
  • WebGpuSurface.kt: [AndroidExternalSurface](/reference/kotlin/androidx/compose/foundation/package-summary#AndroidExternalSurface(androidx.compose.ui.Modifier,kotlin.Boolean,androidx.compose.ui.unit.IntSize,androidx.compose.foundation.AndroidExternalSurfaceZOrder,kotlin.Boolean,kotlin.Function1)) kullanarak kullanıcı arayüzü bileşenini tanımlar. Surface yaşam döngüsü kapsamını yönetir, yüzey hazır olduğunda oluşturucuyu başlatır ve yok edildiğinde temizler.
  • WebGpuRenderer.kt: WebGPU'ya özel tüm mantığı (cihaz oluşturma, ardışık düzen kurulumu) kapsar. Kullanıcı arayüzünden bağımsızdır ve yalnızca çizmek için gereken [Surface](/reference/android/view/Surface.html) ve boyutları alır.

Yaşam döngüsü ve kaynak yönetimi

Yaşam döngüsü yönetimi, Jetpack Compose'da [AndroidExternalSurface](/reference/kotlin/androidx/compose/foundation/package-summary#AndroidExternalSurface(androidx.compose.ui.Modifier,kotlin.Boolean,androidx.compose.ui.unit.IntSize,androidx.compose.foundation.AndroidExternalSurfaceZOrder,kotlin.Boolean,kotlin.Function1)) tarafından sağlanan Kotlin Coroutine kapsamı tarafından ele alınır.

  • Yüzey Oluşturma: onSurface lambda bloğunun başında Device ve Surface yapılandırmasını başlatın. Bu kod, Surface kullanılabilir hale geldiğinde hemen çalışır.
  • Yüzeyin Yok Edilmesi: Kullanıcı sayfadan ayrıldığında veya Surface sistem tarafından yok edildiğinde lambda bloğu iptal edilir. Bellek sızıntılarını önlemek için renderer.cleanup() çağrılarak finally bloğu yürütülür.
  • Yeniden boyutlandırma: Yüzey boyutları değişirse AndroidExternalSurface, yapılandırmaya bağlı olarak bloğu yeniden başlatabilir veya güncellemeleri doğrudan işleyebilir. Böylece oluşturucu her zaman geçerli bir arabelleğe yazar.

Hata ayıklama ve doğrulama

WebGPU, giriş yapılarını doğrulamak ve çalışma zamanı hatalarını yakalamak için tasarlanmış mekanizmalara sahiptir.

  • Logcat: Doğrulama hataları Android Logcat'e yazdırılır.
  • Hata Kapsamları: GPU komutlarını [device.pushErrorScope()](/reference/kotlin/androidx/webgpu/GPUDevice#pushErrorScope(kotlin.Int)) ve device.popErrorScope() blokları içine alarak belirli hataları yakalayabilirsiniz.
device.pushErrorScope(ErrorFilter.Validation)
// ... potentially incorrect code ...
device.popErrorScope { status, type, message ->
    if (status == PopErrorScopeStatus.Success && type != ErrorType.NoError) {
        Log.e("WebGPU", "Validation Error: $message")
    } 
}

Performans ipuçları

WebGPU'da programlama yaparken performans darboğazlarını önlemek için aşağıdakileri göz önünde bulundurun:

  • Kare Başına Nesne Oluşturmaktan Kaçının: Yeniden kullanımı en üst düzeye çıkarmak için uygulama kurulumu sırasında bir kez işlem hatlarını (GPURenderPipeline), bağlama grubu düzenlerini ve gölgelendirici modüllerini oluşturun.
  • Arabellek Kullanımını Optimize Etme: Her karede yeni arabellekler oluşturmak yerine GPUQueue.writeBuffer aracılığıyla mevcut GPUBuffers öğelerinin içeriğini güncelleyin.
  • Durum Değişikliklerini En Aza İndirme: Sürücü ek yükünü en aza indirmek ve oluşturma verimliliğini artırmak için aynı işlem hattını paylaşan ve bağlama gruplarını kullanan çizim çağrılarını gruplandırın.