现象描述:
在使用google map的时候,当从地图界面切换到导航界面的时候,极大概率出现切换过程中黑屏的现象,通过查看log,发现当前出现的错误码也为在hwui中的0x506分析:
launcher花屏的问题也是出现0x506,但是这个0x506的问题已经解决了,这次这个google map的错误码问题应该和花屏的问题不是同一个问题。接着也是通过在hwui中的LayerRenderer中打印log,发现这次错误是出现在创建fbo的时候出错即createLayer的时候:Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque) {
Caches& caches = Caches::getInstance();
GLuint fbo = caches.fboCache.get();
if (!fbo) {
ALOGW("Could not obtain an FBO");
return NULL;
}
caches.activeTexture(0);
Layer* layer = caches.layerCache.get(width, height);
if (!layer) {
ALOGW("Could not obtain a layer");
return NULL;
}
……
GLuint previousFbo;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
glBindFramebuffer(GL_FRAMEBUFFER, layer->getFbo());
layer->bindTexture();
……
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
layer->getTexture(), 0);
glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
return layer;
}
通过打印发现当前绑定到fbo的纹理是无效的,也就是是一个空的纹理,只有id,其他都是没有的。接着通过打印当前的opengl trace,发现当创建fbo的时候,没有调用glBindTexture的操作,也就是说当前的纹理没有被绑定使用。通过代码发现没有被绑定的原因是,在Caches中对纹理绑定进行了优化,也就是如果上一次调用了glBindTexture,且纹理的id一致的话,下次就不需要重新调用glBindTexture,这样可以防止频繁的调用opengl的接口,减少不必要的开销。但是这样的话是不可能导致纹理id无效的,除非当前的纹理id已经被删除了,但是在 Caches维护的id没删除,导致下次进入的时候由于刚好生成了与这个id号一样的纹理,这样就会导致没调用glBindTexture,直接使用上次的纹理,但是上次的却是无效的。
同时通过打印发现出现异常的纹理target比较奇怪,也就是为GL_TEXTURE_EXTERNAL_OES这种类型,接着通过代码发现,也就只有当创建的layer对象为createTextureLayer的时候,才会使用到拓展纹理。接着查看TextureView,发现TextureView的纹理创建在GLConsumer,然而纹理id的创建等都是在hwui中,这样当TextureView的纹理释放的时候(destroySurface): private void destroySurface() {
if (mLayer != null) {
mSurface.detachFromGLContext();
// SurfaceTexture owns the texture name and detachFromGLContext
// should have deleted it
mLayer.clearStorage();
boolean shouldRelease = true;
if (mListener != null) {
shouldRelease = mListener.onSurfaceTextureDestroyed(mSurface);
}
synchronized (mNativeWindowLock) {
nDestroyNativeWindow();
}
mLayer.destroy();
if (shouldRelease) mSurface.release();
mSurface = null;
mLayer = null;
mHadSurface = true;
}
}
首先会先将GLConsumer的纹理内容删除,同时调用glDeleteTextures删除纹理id,接着将会调用clearStorage来调用hwui中的layer的clearTexture:
void Layer::clearTexture() {
texture.id = 0;
}
这时这边只是将纹理的id置为0,并没有将Caches维护的id也置为0,这样下次进行纹理id申请的时候,将会申请到上次删除的id,当调用Caches::bindTexture的时候则将会出现已经绑定了,但是当前的纹理却未绑定,这样也就导致了在使用fbo的时候出现了异常。
修改方案:
当调用layer的clearTexture时,将Caches维护的纹理id也一起清空。
void Layer::clearTexture() {
if (texture.id) {
texture.clearTexture();
texture.id = 0;
}
}
void Caches::clearTexture(GLuint texture) {
for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) {
if (mBoundTextures[i] == texture) {
mBoundTextures[i] = 0;
}
}
}