หากต้องการใช้ Jetpack WebGPU โปรเจ็กต์ของคุณต้องมีคุณสมบัติตรงตามข้อกำหนดขั้นต่ำต่อไปนี้
- ระดับ API ขั้นต่ำ: ต้องใช้ Android API 24 (Nougat) ขึ้นไป
- ฮาร์ดแวร์: เราขอแนะนำให้ใช้แบ็กเอนด์เป็นอุปกรณ์ที่รองรับ Vulkan 1.1 ขึ้นไป
- โหมดความเข้ากันได้และการรองรับ OpenGL ES: คุณใช้ WebGPU กับโหมดความเข้ากันได้ได้โดยตั้งค่าตัวเลือก
featureLevelที่ได้มาตรฐานเป็นcompatibilityขณะขอGPUAdapter
// Example of requesting an adapter with "compatibility" mode enabled:
val adapter = instance.requestAdapter(
GPURequestAdapterOptions(featureLevel = FeatureLevel.Compatibility))
การติดตั้งและการตั้งค่า
สิ่งที่ต้องมีก่อน
Android Studio: ดาวน์โหลด Android Studio เวอร์ชันล่าสุดจากเว็บไซต์อย่างเป็นทางการ แล้วทำตามวิธีการที่ระบุไว้ในคู่มือการติดตั้ง Android Studio
สร้างโปรเจ็กต์ใหม่
เมื่อติดตั้ง Android Studio แล้ว ให้ทำตามขั้นตอนต่อไปนี้เพื่อตั้งค่าโปรเจ็กต์ WebGPU
- เริ่มโปรเจ็กต์ใหม่: เปิด Android Studio แล้วคลิกโปรเจ็กต์ใหม่
เลือกเทมเพลต: เลือกเทมเพลต Empty Activity ใน Android Studio แล้วคลิกถัดไป
รูปที่ 1 การสร้างโปรเจ็กต์ใหม่ใน Android Studio กำหนดค่าโปรเจ็กต์
- ชื่อ: ตั้งชื่อโปรเจ็กต์ (เช่น "JetpackWebGPUSample")
- ชื่อแพ็กเกจ: ตรวจสอบว่าชื่อแพ็กเกจตรงกับเนมสเปซที่คุณเลือก (เช่น com.example.webgpuapp)
- ภาษา: เลือก Kotlin
- SDK ขั้นต่ำ: เลือก API 24: Android 7.0 (Nougat) ขึ้นไปตามที่แนะนำสำหรับไลบรารีนี้
- ภาษาการกำหนดค่าบิลด์: ขอแนะนำให้ใช้ Kotlin DSL (build.gradle.kts) สำหรับการจัดการ Dependency ที่ทันสมัย
รูปที่ 2เริ่มต้นด้วยกิจกรรมที่ว่างเปล่า เสร็จสิ้น: คลิกเสร็จสิ้น แล้วรอให้ Android Studio ซิงค์ไฟล์โปรเจ็กต์
เพิ่มไลบรารี WebGPU Jetpack
- เพิ่มที่เก็บ
googleลงในsettings.gradleตามที่อธิบายไว้ใน ใช้ไลบรารี Jetpack ในแอป - เพิ่มทรัพยากร Dependency สำหรับอาร์ติแฟกต์ที่ต้องการในไฟล์ build.gradle ของแอปหรือโมดูล
- หมายเหตุ: ตรวจสอบเวอร์ชันไลบรารีล่าสุดได้ที่ webgpu | Jetpack | Android Developers
ไลบรารี
androidx.webgpu
มีไฟล์ไลบรารี .so ของ NDK ของ WebGPU รวมถึงอินเทอร์เฟซของโค้ดที่มีการจัดการ
คุณอัปเดตเวอร์ชันของไลบรารีได้โดยการอัปเดต build.gradle และ ซิงค์โปรเจ็กต์กับไฟล์ Gradle โดยใช้ปุ่ม "ซิงค์โปรเจ็กต์" ใน Android Studio
สถาปัตยกรรมระดับสูง
การแสดงผล WebGPU ภายในแอปพลิเคชัน Android จะทำงานบนเธรดการแสดงผลเฉพาะเพื่อรักษาการตอบสนองของ UI
- เลเยอร์ UI: สร้าง UI ด้วย Jetpack Compose ผสานรวมพื้นผิวการวาดภาพ WebGPU
เข้ากับลำดับชั้น Compose โดยใช้
AndroidExternalSurface - ตรรกะการแสดงผล: คลาสเฉพาะ (เช่น WebGpuRenderer) มีหน้าที่รับผิดชอบในการจัดการออบเจ็กต์ WebGPU ทั้งหมดและประสานงานลูปการแสดงผล
- เลเยอร์ Shader: โค้ด Shader ของ WGSL ที่จัดเก็บไว้ในค่าคงที่ res หรือสตริง
ทีละขั้นตอน: แอปตัวอย่าง
ส่วนนี้จะอธิบายขั้นตอนที่จำเป็นในการแสดงผลสามเหลี่ยมสีบนหน้าจอ ซึ่งแสดงให้เห็นเวิร์กโฟลว์หลักของ WebGPU
กิจกรรมหลัก
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
WebGpuSurface()
}
}
}
พื้นผิวภายนอกที่ประกอบได้
สร้างไฟล์ใหม่ชื่อ WebgpuSurface.kt Composable นี้จะห่อหุ้ม
AndroidExternalSurface เพื่อเชื่อมต่อระหว่าง Compose กับตัวแสดงผล
@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()
}
}
}
}
}
ตั้งค่าโปรแกรมแสดงผล
สร้างWebGpuRendererชั้นเรียนใน WebGpuRenderer.kt คลาสนี้จะจัดการ
การสื่อสารกับ GPU
ก่อนอื่น ให้กำหนดโครงสร้างคลาสและตัวแปร
class WebGpuRenderer() {
private lateinit var webGpu: WebGpu
private lateinit var renderPipeline: GPURenderPipeline
}
การเริ่มต้น: จากนั้น ให้ใช้ฟังก์ชัน init เพื่อสร้างอินสแตนซ์ WebGPU
และกำหนดค่าพื้นผิว ฟังก์ชันนี้เรียกใช้โดยAndroidExternalSurfaceขอบเขตภายในคอมโพสภายนอกที่เราสร้างขึ้น
ก่อนหน้านี้
หมายเหตุ: ฟังก์ชัน init ใช้
createWebGpu
เมธอดตัวช่วย (ส่วนหนึ่งของ
androidx.webgpu.helper) เพื่อเพิ่มประสิทธิภาพการตั้งค่า ยูทิลิตีนี้จะสร้างอินสแตนซ์ WebGPU
เลือกอแดปเตอร์ และขออุปกรณ์
// 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 ไลบรารีมีไฟล์ JNI และ .so ซึ่งระบบบิลด์จะลิงก์และจัดการโดยอัตโนมัติ
เมธอด Helper
createWebGpu จะจัดการการโหลด libwebgpu_c_bundled.so ที่รวมไว้
การตั้งค่าไปป์ไลน์
ตอนนี้เรามีอุปกรณ์แล้ว เราต้องบอก GPU วิธีวาดสามเหลี่ยม เราทำเช่นนี้โดยการสร้าง "ไปป์ไลน์" ที่มีโค้ด Shader (เขียนใน WGSL)
เพิ่มฟังก์ชันตัวช่วยส่วนตัวนี้ลงในคลาส WebGpuRenderer เพื่อคอมไพล์เชเดอร์และสร้างไปป์ไลน์การแสดงผล
// 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)
)
)
}
วาดกรอบ
เมื่อไปป์ไลน์พร้อมแล้ว ตอนนี้เราก็สามารถใช้ฟังก์ชันการแสดงผลได้ ฟังก์ชันนี้ จะรับเท็กซ์เจอร์ถัดไปที่พร้อมใช้งานจากหน้าจอ บันทึกคำสั่งการวาด และส่งไปยัง GPU
เพิ่มวิธีการนี้ลงในชั้นเรียน WebGpuRenderer
// 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()
}
การล้างข้อมูลทรัพยากร
ใช้ฟังก์ชันล้างข้อมูลซึ่งเรียกใช้โดย WebGpuSurface เมื่อระบบทำลายพื้นผิว
// Inside WebGpuRenderer class
fun cleanup() {
if (::webGpu.isInitialized) {
webGpu.close()
}
}
เอาต์พุตที่แสดงผล
โครงสร้างแอปตัวอย่าง
แนวทางปฏิบัติที่ดีคือการแยกการติดตั้งใช้งานการแสดงผลออกจากตรรกะ UI ดังเช่นโครงสร้างที่ใช้ในแอปตัวอย่าง
app/src/main/
├── java/com/example/app/
│ ├── MainActivity.kt // Entry point
│ ├── WebGpuSurface.kt // Composable Surface
│ └── WebGpuRenderer.kt // Pure WebGPU logic
- MainActivity.kt: จุดแรกเข้าของแอปพลิเคชัน โดยจะตั้งค่าเนื้อหาเป็น
WebGpuSurfaceComposable - WebGpuSurface.kt: กำหนดคอมโพเนนต์ UI โดยใช้
[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))โดยจะจัดการSurfaceขอบเขตวงจรของ SurfaceView, เริ่มต้นโปรแกรมแสดงผลเมื่อ Surface พร้อม และล้างข้อมูลเมื่อ Surface ถูกทำลาย - WebGpuRenderer.kt: ห่อหุ้มตรรกะทั้งหมดที่เฉพาะเจาะจง WebGPU (การสร้างอุปกรณ์
การตั้งค่าไปป์ไลน์) โดยจะแยกออกจาก UI และรับเฉพาะ
[Surface](/reference/android/view/Surface.html)และมิติข้อมูลที่จำเป็นต่อการวาด
การจัดการวงจรและทรัพยากร
การจัดการวงจรของกิจกรรมจะได้รับการจัดการโดยขอบเขตของ Kotlin Coroutine ที่ [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)) จัดให้ภายใน Jetpack Compose
- การสร้างพื้นผิว: เริ่มต้นการกำหนดค่า
DeviceและSurfaceที่จุดเริ่มต้นของบล็อก LambdaonSurfaceโค้ดนี้จะทำงานทันทีเมื่อSurfaceพร้อมใช้งาน - การทำลายพื้นผิว: เมื่อผู้ใช้นำทางออกไปหรือระบบทำลาย
Surfaceบล็อก Lambda จะถูกยกเลิก ระบบจะfinallyบล็อก และเรียกใช้renderer.cleanup()เพื่อป้องกันหน่วยความจำรั่ว - การปรับขนาด: หากขนาดของพื้นผิวเปลี่ยนแปลง
AndroidExternalSurfaceอาจ รีสตาร์ทบล็อกหรือจัดการการอัปเดตโดยตรง ทั้งนี้ขึ้นอยู่กับการกำหนดค่า เพื่อให้ โปรแกรมแสดงผลเขียนไปยังบัฟเฟอร์ที่ถูกต้องเสมอ
การแก้ไขข้อบกพร่องและการตรวจสอบ
WebGPU มีกลไกที่ออกแบบมาเพื่อตรวจสอบโครงสร้างอินพุตและบันทึกข้อผิดพลาดขณะรันไทม์
- Logcat: ข้อผิดพลาดในการตรวจสอบจะพิมพ์ไปยัง Android Logcat
- ขอบเขตข้อผิดพลาด: คุณสามารถบันทึกข้อผิดพลาดที่เฉพาะเจาะจงได้โดยการห่อหุ้มคำสั่ง GPU
ภายในบล็อก
[device.pushErrorScope()](/reference/kotlin/androidx/webgpu/GPUDevice#pushErrorScope(kotlin.Int))และ `device.popErrorScope()
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")
}
}
เคล็ดลับด้านประสิทธิภาพ
เมื่อเขียนโปรแกรมใน WebGPU ให้พิจารณาสิ่งต่อไปนี้เพื่อหลีกเลี่ยงคอขวดด้านประสิทธิภาพ
- หลีกเลี่ยงการสร้างออบเจ็กต์ต่อเฟรม: สร้างอินสแตนซ์ไปป์ไลน์
(
GPURenderPipeline) เลย์เอาต์กลุ่มการเชื่อมโยง และโมดูล Shader เพียงครั้งเดียวในระหว่างการตั้งค่าแอปพลิเคชันเพื่อเพิ่มการนำกลับมาใช้ใหม่ให้ได้มากที่สุด - เพิ่มประสิทธิภาพการใช้บัฟเฟอร์: อัปเดตเนื้อหาของ
GPUBuffersที่มีอยู่ผ่านGPUQueue.writeBufferแทนที่จะสร้างบัฟเฟอร์ใหม่ในแต่ละเฟรม - ลดการเปลี่ยนแปลงสถานะ: จัดกลุ่มการเรียกใช้การวาดที่ใช้ไปป์ไลน์เดียวกันและเชื่อมโยงกลุ่มเพื่อลดค่าใช้จ่ายของไดรเวอร์และปรับปรุงประสิทธิภาพการแสดงผล