Android

13 nov, 2014

Preparando o libGDX para suporte nativo a CPUs Intel x86 rodando Android

Publicidade

Com dispositivos Android Intel® com arquitetura x86 ganhando destaque no mercado, a equipe do libGDX foi criada para garantir que os desenvolvedores pudessem facilmente implementar seus jogos e aplicativos com o uso de único um framework multiplataforma. Este estudo de caso apresenta uma breve introdução ao libGDX e, em seguida, mostra que pouco esforço é necessário para portar uma enorme base de código existente para dispositivos Android baseados na arquitetura x86!

O que é libGDX?

A libGDX é um framework open-source, utilizado para o desenvolvimento de jogos multiplataforma para Windows, Linux, Mac OS X, Android, iOS, Blackberry e para navegadores com WebGL habilitado. É possível usar libGDX para escrever jogos em qualquer JVM (Java Virtual Machine), linguagem de sua preferência (Java, Scala, Clojure, Kotlin) e pode então implementar a mesma base de código para todas as plataformas suportadas. Através dessas várias plataformas, o libGDX suporta todas as IDEs comumente utilizadas para o desenvolvimento Java, como Eclipse, NetBeans e IntelliJ IDEA. Testes e desenvolvimento em grande parte podem ser feitos em um ambiente desktop, acelerando assim o desenvolvimento e diminuindo o número de iteração, já que o custoso  deploy nos dispositivos é minimizado.

Aficionados, pequenos desenvolvedores e grandes fábricas de software usam libGDX na criação de jogos para dispositivos móveis e desktop, como o Google Ingress, o Halfway da Robotality (Figura 1), ou a linha da Kiwi Inc. de aplicativos para jogos sociais. Aplicativos que não sejam jogos também usam libGDX, como o Esoteric Software Spine 2D, um software para animação de esqueletos.

Figura 1: Tela do jogo Halfway, da Robotality.
Figura 1: Tela do jogo Halfway, da Robotality.

A versão 1.0 do libGDX, lançada no segundo semestre de 2014, teve cerca de 250 mil downloads mensais através do Maven Central, tornando-se um dos mais utilizados frameworks de desenvolvimento de jogos de código aberto e multiplataforma atualmente disponíveis no mercado.

Figura 2: Downloads da libGDX no último ano.
Figura 2: Downloads da libGDX no último ano.

Intel, Android e libGDX

Com essa enorme base de usuários, rapidamente percebemos que o libGDX tinha que suportar perfeitamente dispositivos Android baseados em Intel® x86 e proporcionar o máximo desempenho. Implementamos todos os módulos do libGDX voltados para desempenho crítico em C e C ++, e então os enviamos para desenvolvedores através de binds JNI para que eles pudessem permanecer no conforto de sua linguagem escolhida.

O libGDX apresenta uma ampla gama de componentes nativos de plataformas específicas que ajudam os desenvolvedores a criar jogos de alto desempenho:

  • Gráficos através da CPU: enquanto libGDX depende fortemente de GPU na parte de renderização através de OpenGL ES, ainda há a necessidade da composição de imagem do lado da CPU. Portanto, o libGDX implementa uma biblioteca de renderização 2D e integra FreeType para renderização de fontes.
  • Gerenciamento de memória manual: aplicativos Java apresentam um método de coleta de lixo para melhorar o gerenciamento de memória em tempo de execução. Enquanto isso é conveniente em muitos casos, os recursos de memória para texturas precisam ser gerenciados manualmente. Nós habilitamos o gerenciamento manual, desenvolvendo todas as operações relacionadas com código C++ nativo.
  • Álgebra linear: muitas áreas de desenvolvimento de jogos dependem de cálculo de matrizes, vetores e quaternions. Alguns jogos experimentam problemas de desempenho com multiplicações matriciais. Para ajudar com essas questões, o libGDX implementa operações de álgebra linear comum em C++ e Assembly.
  • Física: a maioria dos jogos exige algum nível de física simulada para gerar mundos interativos críveis. O libGDX integra Box2D e Bullet para física 2D e 3D. Ambas as bibliotecas são escritas em C++ nativo com extensões montadas especificamente para CPU, como Intel® Streaming SIMD Extensions 3 ou NEON.

Desde a sua criação, o libGDX suporta x86 32bits e 64 bits nos sistemas Windows, Linux e Mac OS X. Portanto, estávamos otimistas de que o esforço de portá-lo para Android em x86 seria mínimo. Veja a seguir o que precisava ser feito.

Configurando a compilação para Android x86

Cada um dos componentes nativos do libGDX tem seu próprio subprojeto com arquivos de compilação para cada plataforma. Para sistemas desktop e iOS, contamos com GCC/Clang e um sistema de compilação baseado no Apache Ant. A versão Android do libGDX usa o Kit de Desenvolvimento Nativo Android, o NDK, que é geralmente descrito por dois arquivos: Android.mk e Application.mk. Na maioria dos casos, isso era suficiente para adicionar a plataforma x86 como um destino de compilação no arquivo Application.mk (como mostrado abaixo).

APP_ABI := armeabi armeabi-v7a x86
APP_PLATFORM := android-8
APP_STL := stlport_static

Alguns dos nossos componentes têm flags para compilação com arquitetura específica de CPUs, por exemplo, para habilitar NEON, para utilizar Assembly inline, e assim por diante. Para obter uma primeira compilação funcional, desative todas essas flags.

Testes

Para garantir a estabilidade do código, o libGDX vem com um enorme conjunto de testes que podem ser executados quando estamos expandindo para novas plataformas. Após a primeira compilação NDK para  Android x86, o teste “Bullet” para física foi executado. Foi um grande sucesso! Nossos testes não apresentaram falhas no ASUS Pad MeMO FHD 10 que a Intel nos forneceu para o teste (figura 3).

Figura 3: Exemplo do teste “Bullet Physics” executado em um ASUS Pad MeMO FHD 10.
Figura 3: Exemplo do teste “Bullet Physics” executado em um ASUS Pad MeMO FHD 10.

Todos os outros testes também correram como esperado na CPU, bem como na GPU, dando-nos a confiança de que o Android para x86 estava pronto para os desenvolvedores. Mas poderíamos fazer melhor?

Empregando Vectorização

A maioria dos componentes nativos do libGDX lidam com a matemática de uma forma ou de outra, muitas vezes com valores que podem ser vetorizados pelo GCC automaticamente. Todos os dispositivos Android baseados em x86 disponíveis atualmente suportam Intel SSE3, então vamos garantir que estamos tirando o máximo proveito das nossas CPUs!

ifeq ($(TARGET_ARCH),x86)
  LOCAL_CFLAGS := $(LOCAL_C_FLAGS) -mfpmath=sse -msse3
  LOCAL_CPPFLAGS := $(LOCAL_CPPFLAGS) -mfpmath=sse -msse3
endif

Com o simples truque acima, todo o código nativo agora explora instruções SIMD, desde que o compilador seja capaz de vetorizar o código matemático. Note que não podemos fazer o mesmo para ARM, pois nem todas as plataformas ARM  suportam NEON.

Fazendo desenvolvedores felizes

A partir da versão 1.0, os projetos com libGDX suportam automaticamente dispositivos Android baseados em x86 de forma tão perfeita como suportam qualquer outra plataforma. O suporte adicional é a peça chave do quebra-cabeça para ajudar a tornar os desenvolvedores do libGDX felizes. Nenhuma cópia manual de arquivos ou flags de ajuste é necessária, simplesmente funciona! Se você está interessado no libGDX, recomendamos a leitura de nossa documentação e assistir aos nossos tutoriais em vídeo.

Conclusão

Obter o libGDX e sua enorme base de código nativo para trabalhar com plataformas x86 Android foi uma aventura. Nós simplesmente tivemos que ajustar 2 arquivos (com 5 linhas) por subprojeto. O suporte para dispositivos baseados em x86 com Android e libGDX agora é realidade. Além disso, os desenvolvedores podem explorar ferramentas como o Gerenciador de Execução Aceleradaem Hardware Intel® (HAXM) para melhorar ainda mais o ciclo de desenvolvimento e também utilizar o  Graphics Performance Analizers para fazer profile e bechmark de seus jogos para Android. Essa é uma grande vitória para a comunidade libGDX.

Para conhecer mais do trabalho da Intel com Android, conheça a Comunidade Android no Intel Developer Zone  

*

Artigo traduzido com autorização. O original está em https://software.intel.com/en-us/android/articles/preparing-libgdx-to-natively-support-intel-x86-cpus-running-android