apitraceを使ったfirefoxのWebGLのデバッグ例 - 2018-07-02 - ククログ

ククログ

株式会社クリアコード > ククログ > apitraceを使ったfirefoxのWebGLのデバッグ例

apitraceを使ったfirefoxのWebGLのデバッグ例

はじめに

これまでにも何度か紹介してきましたが、クリアコードではGecko(Firefox)を組み込み機器向けに移植する取り組みを行っています。

当プロジェクトでは移植コストを少しでも低減するために、Firefoxの延長サポート版(ESR)のみを対象としています。これまではESR45やESR52をベースにハードウェアアクセラレーションに対応させるための作業を行ってきました。 現在はESR60に対応し、そのバグを修正する作業を進めています。

この作業に関連するOpenGL ESのデバッグのちょうど良い実例を解説します。 今回のデバッグにはapitraceというOpenGLに関するAPIの呼び出しを取得できるツールを用いています。

実例

apitraceを用いてのデバッグ方法は apitraceを使ったOpenGL ESが絡むFirefoxのデバッグ方法 にて解説しました。

今回はこれを元に、実際にRZ/G1M上でのWebGLの不正な挙動を直すまでに至った修正作業を説明します。

WebGLはFirefox ESR52のときは動作していました。 ESR60へのバージョンアップ時にWebGLのContextが作成されるが、キャンバス描画の更新がうまく行かず、真っ黒となってしまう問題が発生しました。

ESR52のときのEGLのAPIの呼ばれ方とESR60のときのAPIの呼ばれ方を比較してみます。

ESR52の時

...
1127 glBindTexture(target = GL_TEXTURE_2D, texture = 70001)
1128 glTexParameteri(target = GL_TEXTURE_2D, pname = GL_TEXTURE_MIN_FILTER, param = GL_LINEAR)
1129 glTexParameteri(target = GL_TEXTURE_2D, pname = GL_TEXTURE_MAG_FILTER, param = GL_LINEAR)
1130 glTexParameteri(target = GL_TEXTURE_2D, pname = GL_TEXTURE_WRAP_S, param = GL_CLAMP_TO_EDGE)
1131 glTexParameteri(target = GL_TEXTURE_2D, pname = GL_TEXTURE_WRAP_T, param = GL_CLAMP_TO_EDGE)
1132 glTexImage2D(target = GL_TEXTURE_2D, level = 0, internalformat = GL_RGBA, width = 16, height = 16, border = 0, format = GL_RGBA, type = GL_UNSIGNED_BYTE, pixels = NULL)
1133 glBindTexture(target = GL_TEXTURE_2D, texture = 0)
1136 eglGetCurrentContext() = 0x9dc973a0
1137 glGenFramebuffers(n = 1, framebuffers = &70001)
1138 glGetIntegerv(pname = GL_DRAW_FRAMEBUFFER_BINDING, params = &0)
1139 glBindFramebuffer(target = GL_FRAMEBUFFER, framebuffer = 70001)
1140 glFramebufferTexture2D(target = GL_FRAMEBUFFER, attachment = GL_COLOR_ATTACHMENT0, textarget = GL_TEXTURE_2D, texture = 70001, level = 0)
1141 glCheckFramebufferStatus(target = GL_FRAMEBUFFER) = GL_FRAMEBUFFER_COMPLETE
1142 glBindFramebuffer(target = GL_FRAMEBUFFER, framebuffer = 0)
1143 glGetIntegerv(pname = GL_DRAW_FRAMEBUFFER_BINDING, params = &0)
1145 glGenRenderbuffers(n = 1, renderbuffers = &70001)
1146 glGetIntegerv(pname = GL_RENDERBUFFER_BINDING, params = &0)
1147 glBindRenderbuffer(target = GL_RENDERBUFFER, renderbuffer = 70001)
1148 glRenderbufferStorage(target = GL_RENDERBUFFER, internalformat = GL_DEPTH_COMPONENT24, width = 16, height = 16)
1149 glBindRenderbuffer(target = GL_RENDERBUFFER, renderbuffer = 0)
1150 glGenFramebuffers(n = 1, framebuffers = &140002)
1151 glGetIntegerv(pname = GL_DRAW_FRAMEBUFFER_BINDING, params = &0)
1152 glBindFramebuffer(target = GL_FRAMEBUFFER, framebuffer = 140002)
1153 glFramebufferTexture2D(target = GL_FRAMEBUFFER, attachment = GL_COLOR_ATTACHMENT0, textarget = GL_TEXTURE_2D, texture = 70001, level = 0)
1154 glFramebufferRenderbuffer(target = GL_FRAMEBUFFER, attachment = GL_DEPTH_ATTACHMENT, renderbuffertarget = GL_RENDERBUFFER, renderbuffer = 70001)
1155 glBindFramebuffer(target = GL_FRAMEBUFFER, framebuffer = 0)
...

どのAPIも正常に終了していることがわかります。ここで、 glCheckFramebufferStatus(target = GL_FRAMEBUFFER) の戻り値を見てみましょう。

GL_FRAMEBUFFER_COMPLETE とあるので、FrameBufferの状態は正常です。 このように、EGLのAPIの呼ばれ方、終了時の戻り値に着目することでWebGLの動作が正常かどうかの判断をapitraceのダンプから解析できることがわかりました。

ESR60の時

続いて、ESR60についてもダンプデータを解析してみます。

...
57913 glGetFloatv(pname = GL_ALIASED_LINE_WIDTH_RANGE, params = {1, 16})
57914 glGetFloatv(pname = GL_ALIASED_POINT_SIZE_RANGE, params = {1, 511})
57917 eglGetCurrentContext() = 0x830079d0
57919 glGenRenderbuffers(n = 1, renderbuffers = &70001)
57920 glGetIntegerv(pname = GL_RENDERBUFFER_BINDING, params = &0)
57921 glBindRenderbuffer(target = GL_RENDERBUFFER, renderbuffer = 70001)
57922 glRenderbufferStorageMultisampleANGLE(target = GL_RENDERBUFFER, samples = 4, internalformat = GL_RGBA8, width = 300, height = 150)
57923 glBindRenderbuffer(target = GL_RENDERBUFFER, renderbuffer = 0)
57926 glGenFramebuffers(n = 1, framebuffers = &210003)
57927 glGenRenderbuffers(n = 1, renderbuffers = &140002)
57928 glGenRenderbuffers(n = 1, renderbuffers = &210003)
57929 glBindFramebuffer(target = GL_FRAMEBUFFER, framebuffer = 210003)
57930 glFramebufferRenderbuffer(target = GL_FRAMEBUFFER, attachment = GL_COLOR_ATTACHMENT0, renderbuffertarget = GL_RENDERBUFFER, renderbuffer = 70001)
57932 glGetIntegerv(pname = GL_RENDERBUFFER_BINDING, params = &0)
57933 glBindRenderbuffer(target = GL_RENDERBUFFER, renderbuffer = 140002)
57934 glRenderbufferStorageMultisampleANGLE(target = GL_RENDERBUFFER, samples = 4, internalformat = GL_DEPTH24_STENCIL8, width = 300, height = 150)
57935 glBindRenderbuffer(target = GL_RENDERBUFFER, renderbuffer = 0)
57938 glFramebufferRenderbuffer(target = GL_FRAMEBUFFER, attachment = GL_DEPTH_ATTACHMENT, renderbuffertarget = GL_RENDERBUFFER, renderbuffer = 140002)
57939 glFramebufferRenderbuffer(target = GL_FRAMEBUFFER, attachment = GL_STENCIL_ATTACHMENT, renderbuffertarget = GL_RENDERBUFFER, renderbuffer = 140002)
57940 glCheckFramebufferStatus(target = GL_FRAMEBUFFER) = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
...

glRenderbufferStorage の代わりに glRenderbufferStorageMultisampleANGLE が呼ばれています。また、ESR52のダンプの解析と同様に、glCheckFramebufferStatus(target = GL_FRAMEBUFFER) の戻り値を確認します。今度はGL_FRAMEBUFFER_INCOMPLETE_ATTACHMENTとなっているようです。

解析結果から修正方法の検討

  • ESR52の時には glRenderbufferStorage が呼ばれているときは正常に動作していた

  • ESR60の時には glRenderbufferStorageMultisampleANGLE が呼ばれ、glCheckFramebufferStatusのチェックに引っかかってしまった

このことから、WebGLの動作を復元するには glRenderbufferStorageMultisampleANGLE が呼ばれている箇所で glRenderbufferStorage を呼ぶようにすると良さそうです。

glRenderbufferStorageMultisampleANGLE関数は以下の箇所で読み込まれています: https://dxr.mozilla.org/mozilla-esr60/source/gfx/gl/GLContext.cpp#840

Mozillaのコードベースでは、EGLの拡張機能がドライバがサポートしていてもドライバのバグなどにより正常動作を望めない場合に備えて手動でEGLの拡張機能を無効にする方法が提供されています。

void MarkExtensionUnsupported(GLExtensions aKnownExtension) 関数に無効化したい種類の拡張機能のシンボルを与えてやることにより、その拡張機能を無効化できます。

今回無効化したい拡張機能を識別するシンボルは GLFeature::framebuffer_multisample です。

このシンボルにglRenderbufferStorageMultisampleANGLE関数が紐づけられています。

そのため、以下のパッチにより、RZ/G1M上でのWebGLの不正な動作を修正することができました。

diff --git a/gfx/gl/GLContext.cpp b/gfx/gl/GLContext.cpp
index f60ebcaed82e..1186d5efea20 100644
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -743,6 +743,15 @@ GLContext::InitWithPrefixImpl(const char* prefix, bool trygl)
             MarkUnsupported(GLFeature::framebuffer_multisample);
         }
 
+#ifdef MOZ_WAYLAND
+        if (Vendor() == GLVendor::Imagination &&
+            Renderer() == GLRenderer::SGX544MP) {
+            // PowerVR SGX544MP's EGL does not have valid
+            // glRenderbufferStorageMultisampleANGLE implementation, which breaks WebGL.
+            MarkUnsupported(GLFeature::framebuffer_multisample);
+        }
+#endif
+
 #ifdef XP_MACOSX
         // The Mac Nvidia driver, for versions up to and including 10.8,
// don't seem to properly support this. See 814839

まとめ

Firefox ESR60へのバージョンアップに伴うWebGLのEGLのAPIの呼ばれ方の変更により、WebGLのキャンバスが黒くなったままの状態から更新されなくなってしまった不具合を修正するに至ったデバッグの実例を解説しました。