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; } } }