Android 4.3のOpenGL ES 3.0-ETC2テクスチャ圧縮

最近では、Androidの新しいバージョン-4.3。 リリースのかなり前に、最初にGalaxy S4、次にNexus 4でリークがありました。これらのファームウェアでは、OpenGL ES 3.0で動作するライブラリがすぐに見つかりました。 HTC Oneでは、ネイティブのAndroidライブラリで動作し、確認されました(Bluetooth Low Energyのサポートに関する噂もあります)。



そして金曜日の夜に、OTAの更新が2つのデバイス(Nexus 4とNexus 10)に同時に届きました。Nexus7は4.3でまだ更新を受け取りませんでしたが、これはまったく動揺しません(理由-後で説明します)。 もちろん、これを試してみてください。







はじめに-OpenGL ES 3.0の新機能



OpenGL ES 3.0の新しいバージョンには、リストに記載しない多くの新機能があります。これらの機能については、 www.khronos.org / opengles / 3_Xのドキュメントをご覧ください。

また、以下の簡単なプレスリリースで: www.khronos.org/news/press/khronos-releases-opengl-es-3.0-specification

この記事では、OpenGL ES 3.0の最もシンプルで最速の使用機能-テクスチャETC2を圧縮するための新しい標準形式に触れます。



ETC2



ETC2圧縮形式はETC1に基づいて開発され、その動作原理はETC1で使用されていないビットシーケンスに基づいています。 ETC1をETC2に拡張する方法の天才を理解するには、次のドキュメントを読む必要があります。

圧縮アルゴリズムの説明: www.jacobstrom.com/publications/StromPetterssonGH07.pdf

ETC2形式の説明: www.graphicshardware.org/previous/www_2007/presentations/strom-etc2-gh07.pdf



ETC2は、ETC1と同様に4x4ピクセルブロックで動作しますが、各ブロックに特定の圧縮アルゴリズム(通常のETC1または3つの追加アルゴリズムのいずれか)が選択されます。



その結果、ETC2は非常にバランスのとれた圧縮アルゴリズムであることが判明しました。これにより、一部のブロックの境界の鮮明さを維持し、他のブロックの滑らかなグラデーションを歪ませることができます。



ドキュメントからさまざまな画像を圧縮する例を次に示します。







OpenGL ES 3.0の初期化



Android 4.3でのOpenGL ES 3.0の初期化には、追加の操作は必要ありません。 通常のOpenGL ES 2.0コンテキストを作成する必要があります。 GPUがOpenGL ES 3.0をサポートしている場合、AndroidはOpenGL ES 2.0と完全に下位互換性のあるOpenGL ES 3.0コンテキストを自動的に作成します。 つまり、Android 4.3では、OpenGL ES 2.0を使用するすべてのアプリケーションは、実際にはOpenGL ES 3.0で動作します。 結果のコンテキストが3.0であることを確認するには、GL_VERSION行を確認する必要があります。 Android 4.3ソースのサンプルコード: android.googlesource.com/platform/frameworks/base / +/ android- 4.3_r0.9 / libs / hwui / Extensions.cpp

これと、リリース4.3でのOpenGL ES 3.0の使用に関するその他の説明に感謝します: plus.google.com/u/0/+RomainGuy/posts/iJmTjpUfR5E



サンプルJavaコード:



protected int mOpenGLVersionMajor; protected int mOpenGLVersionMinor; String strGLVersion = GLES20.glGetString(GLES20.GL_VERSION); if (strGLVersion != null) { Scanner scanner = new Scanner(strGLVersion); scanner.useDelimiter("[^\\w']+"); int i = 0; while (scanner.hasNext()) { if (scanner.hasNextInt()) { if (i == 0) { mOpenGLVersionMajor = scanner.nextInt(); i++; } if (i == 1) { mOpenGLVersionMinor = scanner.nextInt(); i++; } } if (scanner.hasNext()) { scanner.next(); } } } protected Boolean isES2() { return mOpenGLVersionMajor == 2; } protected Boolean isES3() { return mOpenGLVersionMajor == 3; }
      
      





nVidia



ここでは、おそらく、Nexus 7からAndroid 4.3への更新の遅延を気にしない理由を説明するのが理にかなっています。 実際、nVidia Tegra 2/3チップはOpenGL ES 3.0をサポートしていません。 残念ながら、Tegra 4でもサポートされていません。 NVIDIAは単にデスクトップソリューションをモバイルチップにプッシュし続けており、マーケティング部門はこの遅れのあるソリューションをうまくプッシュしています。 Tegra 4ホワイトペーパー、11ページでこれについてかなりばかげた言い訳は何ですか: www.nvidia.com/docs/IO/116757/Tegra_4_GPU_Whitepaper_FINALv2.pdfアプリやゲームがES 3.0をすぐに使用することは期待していません。」 驚き-Android 4.3自体はOpenGL ES 3.0を使用します。



nVidiaは現在のチップを拡張してES 3.0をサポートする予定はありませんが、Tegra 5は既にサポートしています: www.ubergizmo.com/2013/07/nvidia-tegra-5-release-date-specs-news



圧縮テクスチャを作成する



ETC2テクスチャを作成するために、Maliテクスチャ圧縮ツールが使用されました: malideveloper.arm.com/develop-for-mali/mali-gpu-texture-compression-tool 。 最高品質のテクスチャを得るために、圧縮方法「低速」とエラーメトリック「知覚的」が使用されました。



以下に、ETC1とETC2の圧縮品質を比較するための画像と、元の画像と圧縮された画像の違いを示します。



ETC1でテクスチャを圧縮すると、アーチファクトが水平方向(特にはっきりと見える)と垂直方向のストライプの形で見えます。 ETC2が圧縮されると、これらのアーティファクトは実質的になくなります。

私たちのライブ壁紙では、テクスチャの品質が重要なシーンは非圧縮テクスチャを使用します。 比較画像でわかるように、ETC1は滑らかなグラデーションでテクスチャに最も顕著な歪みを導入します-4x4ピクセルの正方形の圧縮機能によって引き起こされる圧縮アーティファクトがはっきりと見えるようになります。 したがって、空にはテクスチャを圧縮せずに適用します。サイズが2048x512であるため、多くのスペースを占有します。 PVRTC形式での圧縮もかなり良いテクスチャ品質を提供しますが、PowerVRチップでのみ利用可能です。 ES 3.0に標準のETC2形式を使用すると、許容できるテクスチャ品質を達成しながら、テクスチャに割り当てられたビデオメモリの量を4倍削減できました。

テクスチャ2048x512の場合:

非圧縮(16ビットカラー565-ピクセルあたり2バイト):2 * 2048 * 512 = 2097152 // 2 MBのデータ

圧縮(16バイト-PKMヘッダー):524304-16 = 524288 // 512 kBのデータ。



ETC2テクスチャの読み込み



テクスチャは.pkmファイルからロードされます。 ETC1テクスチャの保存には同じ形式が使用されます。 タイトルの形式は次のとおりです: forums.arm.com/index.php? / topic / 15835-pkm-header-format



ETC1Utilにはヘッダー検証があるため、ETC1Utilを使用してETC2テクスチャをロードしないことにしました。

テクスチャをロードするためのコード:



 protected int loadETC2Texture(String filename, int compression, Boolean bClamp, Boolean bHighQuality) { int[] textures = new int[1]; GLES20.glGenTextures(1, textures, 0); int textureID = textures[0]; GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureID); if (bHighQuality) { GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); } else { GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); } if (bClamp) { GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); } else { GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT); } InputStream is = null; try { is = mWallpaper.getContext().getAssets().open(filename); } catch (IOException e1) { e1.printStackTrace(); } try { byte[] data = readFile(is); ByteBuffer buffer = ByteBuffer.allocateDirect(data.length).order(ByteOrder.LITTLE_ENDIAN); buffer.put(data).position(PKM_HEADER_SIZE); ByteBuffer header = ByteBuffer.allocateDirect(PKM_HEADER_SIZE).order(ByteOrder.BIG_ENDIAN); header.put(data, 0, PKM_HEADER_SIZE).position(0); int width = header.getShort(PKM_HEADER_WIDTH_OFFSET); int height = header.getShort(PKM_HEADER_HEIGHT_OFFSET); GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D, 0, compression, width, height, 0, data.length - PKM_HEADER_SIZE, buffer); checkGlError("Loading of ETC2 texture; call to glCompressedTexImage2D()"); } catch (Exception e) { Log.w(TAG, "Could not load ETC2 texture: " + e); } finally { try { is.close(); } catch (IOException e) { // ignore exception thrown from close. } } return textureID; } ... if (isES3()) { textureID = loadETC2Texture("textures/etc2/sky1.pkm", GLES30.GL_COMPRESSED_RGB8_ETC2, false, false); } else { textureID = loadTexture("textures/sky1.png"); } ...
      
      





したがって、ES 3.0コンテキストが存在する場合、ETC2テクスチャがロードされ、ES 2.0モードでは、通常の非圧縮テクスチャがロードされます。



もちろん、GLES30クラスにアクセスするには、アプリケーションマニフェストでandroid:targetSdkVersion = "18"を設定し、project.propertiesでtarget = android-18を設定する必要があります。



結果



アプリケーションでは、非圧縮テクスチャ(歪みなし)とETC2の違いは目立ちません:





アプリケーションは、 play.google.com / store / apps / details?id = org.androidworks.livewallpapercarfreeから入手できます。



おわりに



パフォーマンスを最適化し、機能を拡張するために、Androidの各新バージョンの新機能を常に使用するよう努めています。 特に、壁紙はスクリーンセーバーモードでの作業もサポートしています-白昼夢。 この記事は十分な大きさではないことが判明しましたが、ETC2を使用した私たちのささやかな経験が、誰かがアプリケーションを最適化するのに役立つことを願っています。



PS


誰かがSamsung Galaxy S4のリークされたファームウェア4.3をインストールした場合、このデバイスでこのアプリケーションの動作を確認してください。



All Articles