gl4esの描画異常を追う
とりあえずある程度安定して動作するようになり、Vulkanレベルでのリプレイも取れるようになってきたので、そろそろ安定した描画が得られない原因を探ることにする。
描画に穴が空く / 貼られるテクスチャが異常になる問題
謎の縦縞が表われている。Depthを確認する限りちゃんと透明で抜けているので、フラグメントシェーダか渡されているサンプラ等のUniformが異常なのではないだろうか。
で、ねっとりとリプレイを確認したところ(よくハングするのでめっちゃ時間が掛かった...)、いわゆるトゥーンシェーディングのための影パラメタをテクスチャで渡しているところがあり、それが正常に渡せていない = gl4esのマルチテクスチャ実装が不味そうということがわかった。
このタイトルは(記憶が正しければ)正方形でないテクスチャを使用することは無いはずだが、これらの異常な描画では 512x32 のテクスチャが使われているのでOpenGL側から渡されたテクスチャを正常に受けとっていないことになる。
glBindBuffer
が正常に行われない
... というかそれ以前に、Bufferの挙動がおかしい。
Using VBO 17 for indices
EVENT: glDrawElements(context = 1, mode = GL_TRIANGLES, count = 3528, type = GL_UNSIGNED_SHORT, indices = 0x0000000000000000)
INFO: glDrawElements: GL error: HIGH: No element array buffer and no pointer.
EVENT: glGetError(context = 1)
DUMMY: wglGetPixelFormat (did nothing)
glBindBuffer(GL_ARRAY_BUFFER, 25)
glBufferSubData(GL_ARRAY_BUFFER, 001D93D0, 2016, 1A5AC400)
EVENT: glBufferSubData(context = 1, target = GL_ARRAY_BUFFER, offset = 1938384, size = 2016, data = 0x000000001a5ac400)
EVENT: glGetError(context = 1)
DUMMY: wglGetPixelFormat (did nothing)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 76)
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 00001B90, 48, 22FEB6E0)
EVENT: glBufferSubData(context = 1, target = GL_ELEMENT_ARRAY_BUFFER, offset = 7056, size = 48, data = 0x0000000022feb6e0)
INFO: glBufferSubData: GL error: HIGH: A buffer must be bound.
EVENT: glGetError(context = 1)
EVENT: glGetError(context = 1)
EVENT: glGetError(context = 1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 76)
EVENT: glCheckFramebufferStatus(context = 1, target = GL_FRAMEBUFFER)
glCheckFramebufferStatus(0x8D40)=0x8CD5
glDrawElementsBaseVertex(GL_TRIANGLES, 24, GL_UNSIGNED_SHORT, 00001B90, 0), vtx=20225040 map=2375B140, pending=0
glDrawElements(GL_TRIANGLES, 24, GL_UNSIGNED_SHORT, 00001B90), vtx=20225040 map=2375B140, pending=0
glDrawElementsCommon(GL_TRIANGLES, 0, 24, 0, 2375CCD0, 00000000, 1)
EVENT: glBindTexture(context = 1, target = GL_TEXTURE_2D, texture = 29)
fpe_glDrawElements(GL_TRIANGLES, 24, GL_UNSIGNED_SHORT, 2375CCD0), program=104, instanceID=0
GLSL program 104 need customization => 108
Uniform sync'd with 108 and father (264 uniforms)
Using VBO 17 for indices
EVENT: glDrawElements(context = 1, mode = GL_TRIANGLES, count = 24, type = GL_UNSIGNED_SHORT, indices = 0x0000000000001b90)
ANGLEがたまに
INFO: glBufferSubData: GL error: HIGH: A buffer must be bound.
とエラーを出力しているようだ。gl4esはバッファ関連の操作を一旦遅延して glDrawElements
等のバッファを使用する操作のときに纏めて行う実装になっているが、そこがバグっていることになる。
この辺にブレークを仕込んでデバッグしていくことにする。
glDeleteBuffer
がBufferを暗黙にunbindすることを考慮していない
これは https://github.com/ptitSeb/gl4es/issues/381#issuecomment-1237269621 で指摘されているが、今でも治っていないようだ。
この辺のコードはこのコミットで導入された:
しかし、DeleteBufferだけ対策してもまったく状況が改善しない。
diff --git a/src/gl/buffers.c b/src/gl/buffers.c
index 6770f023..eaf0bc8d 100644
--- a/src/gl/buffers.c
+++ b/src/gl/buffers.c
@@ -162,6 +162,22 @@ void APIENTRY_GL4ES gl4es_glBindBuffer(GLenum target, GLuint buffer) {
noerrorShim();
}
+static void fixup_binding(GLuint buffer){
+ if(! buffer) return; // Debug
+ if(glstate->bind_buffer.array == buffer){
+ printf("I guess array buffer %d was bound and unbound implicitly\n", buffer);
+ glstate->bind_buffer.array = 0;
+ //FIXME: Update used
+
+ }
+ if(glstate->bind_buffer.index == buffer){
+ printf("I guess index buffer %d was bound and unbound implicitly\n", buffer);
+ glstate->bind_buffer.index = 0;
+ //FIXME: Update used, want
+ }
+}
+
+
void APIENTRY_GL4ES gl4es_glBufferData(GLenum target, GLsizeiptr size, const GLvoid * data, GLenum usage) {
DBG(printf("glBufferData(%s, %zi, %p, %s)\n", PrintEnum(target), size, data, PrintEnum(usage));)
if (!buffer_target(target)) {
@@ -186,6 +202,7 @@ void APIENTRY_GL4ES gl4es_glBufferData(GLenum target, GLsizeiptr size, const GLv
rebind_real_buff_arrays(buff->real_buffer, 0);
LOAD_GLES(glDeleteBuffers);
gles_glDeleteBuffers(1, &buff->real_buffer);
+ fixup_binding(buff->real_buffer);
// what about VA already pointing there?
buff->real_buffer = 0;
}
@@ -244,6 +261,7 @@ void APIENTRY_GL4ES gl4es_glNamedBufferData(GLuint buffer, GLsizeiptr size, cons
if(buff->real_buffer && !go_real) {
LOAD_GLES(glDeleteBuffers);
gles_glDeleteBuffers(1, &buff->real_buffer);
+ fixup_binding(buff->real_buffer);
// what about VA already pointing there?
buff->real_buffer = 0;
}
@@ -350,6 +368,7 @@ void APIENTRY_GL4ES gl4es_glDeleteBuffers(GLsizei n, const GLuint * buffers) {
rebind_real_buff_arrays(buff->real_buffer, 0); // unbind
LOAD_GLES(glDeleteBuffers);
gles_glDeleteBuffers(1, &buff->real_buffer);
+ fixup_binding(buff->real_buffer);
}
if (glstate->vao->vertex == buff)
glstate->vao->vertex = NULL;
仕様 https://registry.khronos.org/OpenGL-Refpages/es3.0/html/glBindBuffer.xhtml では
When a buffer object is bound to a target, the previous binding for that target is automatically broken.
なので、array bufferだったのが index bufferに代わるようなケースも対策しないとダメなんだろうか。。ただ基本的にbufferの種別を途中で変えるのは非効率なので避けるべきと言える。 → 対策してもダメだった
Attributeの設定がたまに抜ける
ログ https://gist.github.com/okuoku/fefff02b3d76201f2c91572d02df6699#file-crashlog-txt-L9647-L9988 によると、この2つの glDrawArrays
の間に再度Bufferを作り直しているが、その間に行われた glVertexAttribPointer
をやりなおしている形跡がない。
(ANGLEのログには EVENT:
がプレフィックスされているので、それが無いログは gl4es がエミュレートしている呼出しということになる。)
Bufferの同一性チェックを番号比較で行うのはダメで、タイムスタンプなり何なりを振らなければならない。
... まぁとりあえずこのキャッシュ自体を無効化するか。。
diff --git a/src/gl/fpe.c b/src/gl/fpe.c
index e9a8cad4..36bed57c 100644
--- a/src/gl/fpe.c
+++ b/src/gl/fpe.c
@@ -1437,6 +1437,7 @@ void realize_glenv(int ispoint, int first, int count, GLenum type, const void* i
else
gles_glDisableVertexAttribArray(i);
}
+ dirty = 1; // DEBUG
// check if new value has to be sent to hardware
if(v->enabled) {
// array case
文字列のテクスチャだった
512x32 のテクスチャを再度確認してみると、これは文字列のテクスチャだった。このタイトルはいわゆるテクスチャアトラスをGPU側に持たせるのではなく、都度CPUで描画してアップロードする形を取っている。
背景を描画するタイミングではこれは不要なので、明かに間違っている。... ということは、Buffer同様にテクスチャも取り違える何かがあるのか。。?