Để sử dụng Jetpack WebGPU, dự án của bạn phải đáp ứng các yêu cầu tối thiểu sau:
- Cấp độ API tối thiểu: Bắt buộc phải là API Android 24 (Nougat) trở lên.
- Phần cứng: Các thiết bị hỗ trợ Vulkan 1.1 trở lên được ưu tiên cho phần phụ trợ.
- Chế độ tương thích và khả năng hỗ trợ OpenGL ES: Bạn có thể sử dụng WebGPU với chế độ tương thích bằng cách đặt lựa chọn
featureLevelđược chuẩn hoá thànhcompatibilitytrong khi yêu cầuGPUAdapter.
// Example of requesting an adapter with "compatibility" mode enabled:
val adapter = instance.requestAdapter(
GPURequestAdapterOptions(featureLevel = FeatureLevel.Compatibility))
Cài đặt và thiết lập
Điều kiện tiên quyết:
Android Studio: Tải phiên bản mới nhất của Android Studio xuống từ trang web chính thức và làm theo hướng dẫn trong Hướng dẫn cài đặt Android Studio.
Tạo dự án mới
Sau khi cài đặt Android Studio, hãy làm theo các bước sau để thiết lập dự án WebGPU:
- Bắt đầu một dự án mới: Mở Android Studio rồi nhấp vào Dự án mới.
Chọn một mẫu: Chọn mẫu Empty Activity (Hoạt động trống) trong Android Studio rồi nhấp vào Next (Tiếp theo).
Hình 1.Tạo một dự án mới trong Android Studio Định cấu hình dự án:
- Tên: Đặt tên cho dự án của bạn (ví dụ: "JetpackWebGPUSample").
- Package Name (Tên gói): Xác minh rằng tên gói khớp với không gian tên bạn chọn (ví dụ: com.example.webgpuapp).
- Ngôn ngữ: Chọn Kotlin.
- Minimum SDK (SDK tối thiểu): Chọn API 24: Android 7.0 (Nougat) trở lên, theo đề xuất cho thư viện này.
- Ngôn ngữ cấu hình bản dựng: Bạn nên sử dụng Kotlin DSL (build.gradle.kts) để quản lý các phần phụ thuộc hiện đại.
Hình 2.Bắt đầu bằng một hoạt động trống Kết thúc: Nhấp vào Kết thúc rồi chờ Android Studio đồng bộ hoá các tệp dự án.
Thêm thư viện WebGPU Jetpack
- Thêm kho lưu trữ
googlevàosettings.gradlenhư mô tả trong phần Dùng một thư viện Jetpack trong ứng dụng của bạn - Thêm các phần phụ thuộc cho cấu phần phần mềm bạn cần trong tệp build.gradle cho ứng dụng hoặc mô-đun của bạn:
- Lưu ý: Hãy xem webgpu | Jetpack | Nhà phát triển Android để biết phiên bản thư viện mới nhất
Thư viện androidx.webgpu chứa các tệp thư viện .so WebGPU NDK cũng như các giao diện mã được quản lý.
Bạn có thể cập nhật phiên bản thư viện bằng cách cập nhật build.gradle và đồng bộ hoá dự án với các tệp gradle bằng nút "Sync Project" (Đồng bộ hoá dự án) trong Android Studio.
Kiến trúc cấp cao
Quá trình kết xuất WebGPU trong một ứng dụng Android được chạy trên một luồng kết xuất chuyên dụng để duy trì khả năng phản hồi của giao diện người dùng.
- Lớp giao diện người dùng: Giao diện người dùng được tạo bằng Jetpack Compose. Một bề mặt vẽ WebGPU được tích hợp vào hệ phân cấp Compose bằng cách sử dụng
AndroidExternalSurface. - Logic hiển thị: Một lớp chuyên biệt (ví dụ: WebGpuRenderer) chịu trách nhiệm quản lý tất cả các đối tượng WebGPU và điều phối vòng lặp kết xuất.
- Lớp chương trình đổ bóng: Mã chương trình đổ bóng WGSL được lưu trữ trong các hằng số res hoặc chuỗi.
Từng bước: ứng dụng mẫu
Phần này trình bày các bước cần thiết để kết xuất một hình tam giác có màu trên màn hình, minh hoạ quy trình làm việc cốt lõi của WebGPU.
Hoạt động chính
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
WebGpuSurface()
}
}
}
Thành phần kết hợp bề mặt bên ngoài
Tạo một tệp mới có tên là WebgpuSurface.kt. Thành phần kết hợp này bao bọc AndroidExternalSurface để cung cấp cầu nối giữa Compose và trình kết xuất của bạn.
@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()
}
}
}
}
}
Thiết lập trình kết xuất
Tạo một lớp WebGpuRenderer trong WebGpuRenderer.kt. Lớp này sẽ xử lý phần lớn công việc giao tiếp với GPU.
Trước tiên, hãy xác định cấu trúc lớp và các biến:
class WebGpuRenderer() {
private lateinit var webGpu: WebGpu
private lateinit var renderPipeline: GPURenderPipeline
}
Khởi chạy: Tiếp theo, hãy triển khai hàm init để tạo thực thể WebGPU và định cấu hình bề mặt. Hàm này được gọi theo phạm vi AndroidExternalSurface bên trong thành phần kết hợp bề mặt bên ngoài mà chúng ta đã tạo trước đó.
Lưu ý: Hàm init sử dụng createWebGpu, một phương thức trợ giúp (một phần của androidx.webgpu.helper) để đơn giản hoá quá trình thiết lập. Tiện ích này tạo phiên bản WebGPU, chọn một bộ chuyển đổi và yêu cầu một thiết bị.
// 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,
)
)
}
Thư viện androidx.webgpu bao gồm các tệp JNI và .so, được hệ thống xây dựng tự động liên kết và quản lý. Phương thức trợ giúp createWebGpu sẽ lo việc tải libwebgpu_c_bundled.so theo gói.
Thiết lập quy trình
Giờ đây, khi đã có một thiết bị, chúng ta cần cho GPU biết cách vẽ hình tam giác. Chúng ta thực hiện việc này bằng cách tạo một "quy trình" chứa mã chương trình đổ bóng (được viết bằng WGSL).
Thêm hàm trợ giúp riêng tư này vào lớp WebGpuRenderer để biên dịch các chương trình đổ bóng và tạo quy trình kết xuất.
// 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)
)
)
}
Vẽ một khung hình
Khi quy trình đã sẵn sàng, giờ đây, chúng ta có thể triển khai hàm kết xuất. Hàm này lấy hoạ tiết có sẵn tiếp theo từ màn hình, ghi lại các lệnh vẽ và gửi chúng đến GPU.
Thêm phương thức này vào lớp 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()
}
Dọn dẹp tài nguyên
Triển khai hàm dọn dẹp. Hàm này được WebGpuSurface gọi khi bề mặt bị huỷ.
// Inside WebGpuRenderer class
fun cleanup() {
if (::webGpu.isInitialized) {
webGpu.close()
}
}
Kết quả hiển thị
Cấu trúc ứng dụng mẫu
Bạn nên tách riêng quá trình triển khai kết xuất khỏi logic giao diện người dùng, như trong cấu trúc mà ứng dụng mẫu sử dụng:
app/src/main/
├── java/com/example/app/
│ ├── MainActivity.kt // Entry point
│ ├── WebGpuSurface.kt // Composable Surface
│ └── WebGpuRenderer.kt // Pure WebGPU logic
- MainActivity.kt: Điểm truy cập của ứng dụng. Thao tác này sẽ đặt nội dung thành
WebGpuSurfaceComposable. - WebGpuSurface.kt: Xác định thành phần giao diện người dùng bằng
[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)). Lớp này quản lý phạm vi vòng đờiSurface, khởi chạy trình kết xuất khi bề mặt đã sẵn sàng và dọn dẹp khi bề mặt bị huỷ. - WebGpuRenderer.kt: Đóng gói tất cả logic dành riêng cho WebGPU (Tạo thiết bị, thiết lập Pipeline). Thành phần này tách biệt với giao diện người dùng, chỉ nhận
[Surface](/reference/android/view/Surface.html)và các phương diện cần thiết để vẽ.
Quản lý vòng đời và tài nguyên
Việc quản lý vòng đời do phạm vi Coroutine Kotlin cung cấp bởi [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)) trong Jetpack Compose xử lý.
- Tạo vùng hiển thị: Khởi chạy cấu hình
DevicevàSurfacekhi bắt đầu khối lambdaonSurface. Mã này sẽ chạy ngay lập tức khiSurfacecó sẵn. - Huỷ bỏ bề mặt: Khi người dùng rời khỏi hoặc
Surfacebị hệ thống huỷ, khối lambda sẽ bị huỷ. Khốifinallyđược thực thi, gọirenderer.cleanup()để ngăn rò rỉ bộ nhớ. - Đổi kích thước: Nếu kích thước bề mặt thay đổi,
AndroidExternalSurfacecó thể khởi động lại khối hoặc trực tiếp xử lý các bản cập nhật tuỳ thuộc vào cấu hình, do đó, trình kết xuất luôn ghi vào một vùng đệm hợp lệ.
Gỡ lỗi và xác thực
WebGPU có các cơ chế được thiết kế để xác thực cấu trúc đầu vào và ghi lại các lỗi thời gian chạy.
- Logcat: Lỗi xác thực được in vào Android Logcat.
- Phạm vi lỗi: Bạn có thể ghi lại các lỗi cụ thể bằng cách đóng gói các lệnh GPU trong các khối
[device.pushErrorScope()](/reference/kotlin/androidx/webgpu/GPUDevice#pushErrorScope(kotlin.Int))và 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")
}
}
Mẹo tăng hiệu suất
Khi lập trình trong WebGPU, hãy cân nhắc những điều sau để tránh tình trạng tắc nghẽn hiệu suất:
- Tránh tạo đối tượng theo khung hình: Tạo bản sao của các quy trình (
GPURenderPipeline), liên kết bố cục nhóm và mô-đun chương trình đổ bóng một lần trong quá trình thiết lập ứng dụng để tối đa hoá khả năng sử dụng lại. - Tối ưu hoá việc sử dụng vùng đệm: Cập nhật nội dung của
GPUBuffershiện có thông quaGPUQueue.writeBufferthay vì tạo vùng đệm mới cho mỗi khung hình. - Giảm thiểu các thay đổi về trạng thái: Nhóm các lệnh gọi vẽ dùng chung cùng một quy trình và nhóm liên kết để giảm thiểu chi phí của trình điều khiển và cải thiện hiệu quả kết xuất.