Para usar o Jetpack WebGPU, seu projeto precisa atender aos seguintes requisitos mínimos:
- Nível mínimo da API: é necessário usar a API Android 24 (Nougat) ou mais recente.
- Hardware: dispositivos compatíveis com Vulkan 1.1 ou mais recente são preferíveis para o back-end.
- Modo de compatibilidade e suporte ao OpenGL ES: é possível usar o WebGPU com o modo de compatibilidade
ao definir a opção padronizada
featureLevelcomocompatibilityao solicitar oGPUAdapter.
// Example of requesting an adapter with "compatibility" mode enabled:
val adapter = instance.requestAdapter(
GPURequestAdapterOptions(featureLevel = FeatureLevel.Compatibility))
Instalação e configuração
Pré-requisitos:
Android Studio: faça o download da versão mais recente do Android Studio no site oficial e siga as instruções do Guia de instalação do Android Studio.
Criar um novo projeto
Depois de instalar o Android Studio, siga estas etapas para configurar seu projeto WebGPU:
- Iniciar um novo projeto: abra o Android Studio e clique em Novo projeto.
Selecionar um modelo: escolha o modelo Empty Activity no Android Studio e clique em Next.
Figura 1.Criando um novo projeto no Android Studio Configure seu projeto:
- Nome: dê um nome ao projeto (por exemplo, "JetpackWebGPUSample").
- Nome do pacote: verifique se o nome do pacote corresponde ao namespace escolhido (por exemplo, com.example.webgpuapp).
- Linguagem: selecione Kotlin.
- Minimum SDK: selecione API 24: Android 7.0 (Nougat) ou uma versão mais recente, conforme recomendado para essa biblioteca.
- Linguagem de configuração do build: é recomendável usar a DSL do Kotlin (build.gradle.kts) para o gerenciamento moderno de dependências.
Figura 2.Como começar com uma atividade vazia Concluir: clique em Concluir e aguarde até que o Android Studio sincronize os arquivos do projeto.
Adicionar a biblioteca WebGPU Jetpack
- Adicione o repositório
googleaosettings.gradle, conforme descrito em Usar uma biblioteca do Jetpack no seu app - Adicione as dependências para os artefatos necessários no arquivo build.gradle do seu app ou módulo:
- Observação: confira webgpu | Jetpack | Desenvolvedores Android para acessar a versão mais recente da biblioteca.
A biblioteca
androidx.webgpu
contém os arquivos de biblioteca .so do NDK WebGPU e as interfaces
de código gerenciado.
Para atualizar a versão da biblioteca, atualize o build.gradle e sincronize o projeto com os arquivos do Gradle usando o botão "Sync Project" no Android Studio.
Arquitetura de alto nível
A renderização da WebGPU em um aplicativo Android é executada em uma linha de execução de renderização dedicada para manter a capacidade de resposta da interface.
- Camada de UI: a interface é criada com o Jetpack Compose. Uma superfície de desenho da WebGPU
é integrada à hierarquia do Compose usando
AndroidExternalSurface. - Lógica de renderização: uma classe especializada (por exemplo, O WebGpuRenderer) é responsável por gerenciar todos os objetos WebGPU e coordenar o loop de renderização.
- A camada de shader: código de shader WGSL armazenado em constantes res ou string.
Por etapas: app de exemplo
Esta seção mostra as etapas essenciais necessárias para renderizar um triângulo colorido na tela, demonstrando o fluxo de trabalho principal do WebGPU.
A atividade principal
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
WebGpuSurface()
}
}
}
O elemento combinável da superfície externa
Crie um novo arquivo chamado WebgpuSurface.kt. Esse elemento combinável envolve o
AndroidExternalSurface para fornecer uma ponte entre o Compose e seu renderizador.
@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()
}
}
}
}
}
Configurar o renderizador
Crie uma classe WebGpuRenderer em WebGpuRenderer.kt. Essa classe vai lidar com o
trabalho pesado de comunicação com a GPU.
Primeiro, defina a estrutura da classe e as variáveis:
class WebGpuRenderer() {
private lateinit var webGpu: WebGpu
private lateinit var renderPipeline: GPURenderPipeline
}
Inicialização:em seguida, implemente a função init para criar a instância
WebGPU e configurar a superfície. Essa função é chamada pelo escopo AndroidExternalSurface dentro do elemento combinável de superfície externa que criamos anteriormente.
Observação:a função init usa
createWebGpu,
um método auxiliar (parte de
androidx.webgpu.helper) para simplificar a configuração. Essa utilidade cria a instância do WebGPU, seleciona um adaptador e solicita um dispositivo.
// 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,
)
)
}
A biblioteca androidx.webgpu inclui arquivos JNI e .so, que são
vinculados e gerenciados automaticamente pelo sistema de build. O método auxiliar
createWebGpu carrega o libwebgpu_c_bundled.so agrupado.
Configuração do pipeline
Agora que temos um dispositivo, precisamos informar à GPU como desenhar o triângulo. Para isso, criamos um "pipeline" que contém nosso código de shader (escrito em WGSL).
Adicione esta função auxiliar particular à classe WebGpuRenderer para compilar os
shaders e criar o pipeline de renderização.
// 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)
)
)
}
Desenhar um frame
Com o pipeline pronto, podemos implementar a função de renderização. Essa função adquire a próxima textura disponível da tela, grava comandos de desenho e os envia para a GPU.
Adicione este método à classe 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()
}
Limpeza de recursos
Implemente a função de limpeza, que é chamada pelo WebGpuSurface quando a
superfície é destruída.
// Inside WebGpuRenderer class
fun cleanup() {
if (::webGpu.isInitialized) {
webGpu.close()
}
}
Saída renderizada
Estrutura do app de exemplo
É recomendável separar a implementação de renderização da lógica da interface do usuário, como na estrutura usada pelo app de exemplo:
app/src/main/
├── java/com/example/app/
│ ├── MainActivity.kt // Entry point
│ ├── WebGpuSurface.kt // Composable Surface
│ └── WebGpuRenderer.kt // Pure WebGPU logic
- MainActivity.kt: o ponto de entrada do aplicativo. Ele define o conteúdo como o elemento combinável
WebGpuSurface. - WebGpuSurface.kt: define o componente de UI usando
[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)). Ele gerencia o escopo do ciclo de vida doSurface, inicializando o renderizador quando a superfície está pronta e limpando quando ela é destruída. - WebGpuRenderer.kt: encapsula toda a lógica específica da WebGPU (criação de dispositivos, configuração de pipelines). Ele é desacoplado da interface, recebendo apenas o
[Surface](/reference/android/view/Surface.html)e as dimensões necessárias para renderizar.
Gerenciamento de recursos e ciclo de vida
O gerenciamento do ciclo de vida é processado pelo escopo de corrotina do Kotlin fornecido por
[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)) no Jetpack Compose.
- Criação de superfície: inicialize a configuração
DeviceeSurfaceno início do bloco lambdaonSurface. Esse código é executado imediatamente quando oSurfacefica disponível. - Destruição da superfície: quando o usuário sai da navegação ou o
Surfaceé destruído pelo sistema, o bloco lambda é cancelado. Um blocofinallyé executado, chamandorenderer.cleanup()para evitar vazamentos de memória. - Redimensionamento: se as dimensões da superfície mudarem, o
AndroidExternalSurfacepoderá reiniciar o bloco ou processar atualizações diretamente, dependendo da configuração. Assim, o renderizador sempre grava em um buffer válido.
Depuração e validação
A WebGPU tem mecanismos projetados para validar estruturas de entrada e capturar erros de tempo de execução.
- Logcat:os erros de validação são impressos no Logcat do Android.
- Escopos de erro:é possível capturar erros específicos encapsulando comandos de GPU em blocos
[device.pushErrorScope()](/reference/kotlin/androidx/webgpu/GPUDevice#pushErrorScope(kotlin.Int))e 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")
}
}
Dicas de desempenho
Ao programar em WebGPU, considere o seguinte para evitar gargalos de performance:
- Evite a criação de objetos por frame: instancie pipelines (
GPURenderPipeline), vincule layouts de grupos e módulos de shader uma vez durante a configuração do aplicativo para maximizar a reutilização. - Otimizar o uso do buffer: atualize o conteúdo dos
GPUBuffersatuais usandoGPUQueue.writeBufferem vez de criar novos buffers a cada frame. - Minimizar mudanças de estado: agrupe chamadas de desenho que compartilham o mesmo pipeline e vincule grupos para minimizar a sobrecarga do driver e melhorar a eficiência da renderização.