gl4esのレガシOpenGLエミュレーションを疑う会
... というわけで、とりあえずgl4esが行っているレガシOpenGLエミュレーションが怪しいということで動作を追ってみることにする。
(DirectX9くらいの機能に相当するOpenGL ES2.0やWebGLではシェーダを使わないと描画できないが、DirectX9自体には一応固定機能パイプラインも残っている。このため、DirectX9をエミュレーションするためには固定機能パイプラインを活用する必要がある。Wined3dがDirectX9 → OpenGL2.1への変換は行ってくれて、そちらにはそれなりの実績があるので今のところ疑っていない。)
prev
生成されるシェーダ
生成されるシェーダを抜きとっておく。
頂点シェーダ
#version 120
invariant gl_Position;
attribute vec4 vs_in0;
attribute vec4 vs_in1;
attribute float vs_in2;
attribute vec3 vs_in3;
attribute float vs_in4;
attribute vec4 vs_in5;
attribute vec4 vs_in6;
attribute vec4 vs_in7;
attribute vec4 vs_in8;
attribute vec4 vs_in9;
attribute vec4 vs_in10;
attribute vec4 vs_in11;
attribute vec4 vs_in12;
attribute vec4 vs_in13;
attribute vec4 vs_in14;
uniform mat4 ffp_modelview_matrix[4];
uniform mat4 ffp_projection_matrix;
uniform mat3 ffp_normal_matrix;
uniform mat4 ffp_texture_matrix[8];
uniform struct
{
vec4 emissive;
vec4 ambient;
vec4 diffuse;
vec4 specular;
float shininess;
} ffp_material;
uniform vec3 ffp_light_ambient;
uniform struct
{
vec4 diffuse;
vec4 specular;
vec4 ambient;
vec4 position;
vec3 direction;
float range;
float falloff;
float c_att;
float l_att;
float q_att;
float cos_htheta;
float cos_hphi;
} ffp_light[8];
vec4 ffp_varying_diffuse;
vec4 ffp_varying_specular;
vec4 ffp_varying_texcoord[8];
float ffp_varying_fogcoord;
void main()
{
float m;
vec3 r;
vec4 ffp_attrib_position = vs_in0;
vec4 ffp_attrib_blendweight = vs_in1;
vec3 ffp_attrib_normal = vs_in3;
float ffp_attrib_psize = vs_in4;
vec4 ffp_attrib_diffuse = vs_in5.zyxw;
vec4 ffp_attrib_specular = vs_in6.zyxw;
vec4 ffp_attrib_texcoord0 = vs_in7;
vec4 ffp_attrib_texcoord1 = vs_in8;
vec4 ffp_attrib_texcoord2 = vs_in9;
vec4 ffp_attrib_texcoord3 = vs_in10;
vec4 ffp_attrib_texcoord4 = vs_in11;
vec4 ffp_attrib_texcoord5 = vs_in12;
vec4 ffp_attrib_texcoord6 = vs_in13;
vec4 ffp_attrib_texcoord7 = vs_in14;
ffp_attrib_blendweight[0] = 1.0;
vec4 ec_pos = vec4(ffp_attrib_position.xyz, 1.0);
gl_Position = ffp_projection_matrix * ec_pos;
if (ffp_attrib_position.w != 0.0) gl_Position /= ffp_attrib_position.w;
vec3 normal = vec3(0.0);
ffp_varying_diffuse = ffp_attrib_diffuse;
ffp_varying_specular = ffp_attrib_specular;
gl_FrontColor = ffp_varying_diffuse;
gl_FrontSecondaryColor = ffp_varying_specular;
ffp_varying_texcoord[0] = ffp_texture_matrix[0] * ffp_attrib_texcoord0;
gl_TexCoord[0] = ffp_varying_texcoord[0];
ffp_varying_texcoord[1] = ffp_texture_matrix[1] * ffp_attrib_texcoord1;
gl_TexCoord[1] = ffp_varying_texcoord[1];
ffp_varying_texcoord[2] = ffp_texture_matrix[2] * ffp_attrib_texcoord2;
gl_TexCoord[2] = ffp_varying_texcoord[2];
ffp_varying_texcoord[3] = ffp_texture_matrix[3] * ffp_attrib_texcoord3;
gl_TexCoord[3] = ffp_varying_texcoord[3];
ffp_varying_texcoord[4] = ffp_texture_matrix[4] * ffp_attrib_texcoord4;
gl_TexCoord[4] = ffp_varying_texcoord[4];
ffp_varying_texcoord[5] = ffp_texture_matrix[5] * ffp_attrib_texcoord5;
gl_TexCoord[5] = ffp_varying_texcoord[5];
ffp_varying_texcoord[6] = ffp_texture_matrix[6] * ffp_attrib_texcoord6;
gl_TexCoord[6] = ffp_varying_texcoord[6];
ffp_varying_texcoord[7] = ffp_texture_matrix[7] * ffp_attrib_texcoord7;
gl_TexCoord[7] = ffp_varying_texcoord[7];
}
フラグメントシェーダ
#version 120
vec4 tmp0, tmp1;
vec4 ret;
vec4 arg0, arg1, arg2;
uniform sampler2D ps_sampler0;
vec4 tex0;
uniform vec4 specular_enable;
uniform struct
{
vec4 color;
float density;
float end;
float scale;
} ffp_fog;
vec4 ffp_varying_diffuse;
vec4 ffp_varying_specular;
vec4 ffp_varying_texcoord[8];
vec4 ffp_texcoord[8];
float ffp_varying_fogcoord;
void main()
{
ffp_varying_diffuse = gl_Color;
ffp_varying_specular = gl_SecondaryColor;
ffp_texcoord[0] = gl_TexCoord[0];
tex0 = texture2D(ps_sampler0, ffp_texcoord[0].xy);
ret = ffp_varying_diffuse;
ret = tex0 * ffp_varying_diffuse;
gl_FragData[0] = ffp_varying_specular * specular_enable + ret;
}
生成コードを読む
頂点シェーダもフラグメントシェーダも多少の無駄がある。この辺は常識的なGPUドライバであれば最適化で消えるのでそこまで気にする必要は無いがちょっと読みづらい。
ただ、生成されたコード自体には間違いは見当たらない。例えば、頂点シェーダで生成したvaryingをフラグメントシェーダで消費しないのは違法ではない。
リプレイと突き合わせて与えているuniformが正しいかどうかを確認するのが良いかな。
シェーダデバッガでは正常に見える
... RenderDocのシェーダデバッガで実行すると正常に見える。つまり、RenderDocのMesh viewとシェーダーデバッガの実行結果が食い違っている。。そんなこと有るのか。。?
ここで生成されたSPIR-Vの逆アセンブルは
struct _61 : [[Block]] {
float4 _child0 : [[BuiltIn(Position), Invariant]]; // ★ Position指定
}
Output struct61* _63;
のようになっているので、 _63._child0
が gl_Position
に相当する。... ANGLE側でDebugInfoを残せないか調べた方が良いな。。
ANGLE上でSPIRVのDebugInfoを削除するのは https://chromium.googlesource.com/angle/angle/+/8b80e852bd6c2325ba7a00c89b259972c17ee219^!/ で実装されていた。 EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE
をちゃんとしたパスで渡すのは面倒だから、ANGLE側で常にTrueになるようにパッチしちゃう方が簡単かな。
シェーダデバッガが正しかった
SPIR-Vにデバッグ情報を入れて色々確認してみた。Uniformも正常で、かつコードも期待通りだったが、
そもそも正常にレンダリングできていた。。なのでフレームバッファのblitが怪しいことになる。
フレームバッファのblitがおかしい
今回のレンダリングは2つのcolor passで構成されている。
前半が通常のレンダリングで、後半はwined3dがエミュレートしたDirectX的なswapchainになっている。で、前半は正常にレンダリングが行えている(タイムライン下部の青い ▲ がフレームバッファとしてのテクスチャへの書き込みを表わしている)。
が、このテクスチャが後半のcolor passで一切参照されていない というかタイムライン上にreadが一切無い ことからわかるように、blitするテクスチャを取り違えている可能性が高い。
blitは巾120ピクセル高さ1ピクセルの謎の領域に対して行っている。この謎の領域の出どころを探すのが良いかな。
Framebuffer を正常にエミュレートできていない
巾120ピクセル高さ1ピクセルは、Framebuffer 0、つまりスクリーンのことだった。
ここをステップ実行したところ、普段の実行中は texture 13 を Framebuffer 0 にblitする状況になっていた。
... というかキャプチャをリプレイするときに 120x1 のsurfaceの存在が報告されてるな。。EGLの設定がおかしい。。?
[gfxrecon] INFO - Skipping creation for swapchain (ID = 0), which is backed by a disabled surface
[gfxrecon] INFO - Creating 3 images of 120x1 to back dummy swapchain (ID = 15)
試しにtexture 13をアタッチされたFramebufferには切り替えられないようにしてblitすると上下反転した絵が出た。EGL自体は期待通り動作していると言える。このため、多分この120x1は起動時のprobingや初期化中に発生したFlipのものだろう。で、ダイナミックにswapchainのサイズを変化させるコードをこちらでは実装していないので、たぶんgl4esのGLX関連から持ってくる必要がある。
が、これはOpenGL ES的にはどちらもTexture 13への書き込みになっているはずなので、なぜANGLE内で別々のバッファになってしまっているのかを調べないといけない。。
一旦あきらめ
とりあえずRender-to-textureを諦めれば絵は出るので、一旦それでワークアラウンドして残りの問題を潰していくことに。
ANGLEのEGLには画像を上下反転して表示する拡張 EGL_ANGLE_surface_orientation
https://github.com/google/angle/blob/main/extensions/EGL_ANGLE_surface_orientation.txt があるため、それを使える。
単にアトリビュート EGL_SURFACE_ORIENTATION_ANGLE
を設定すれば良い。