我有一個具有兩個線程的Android OpenGL-ES應用程序。將線程1稱爲「顯示線程」,其將其當前紋理與從線程2 a.k.a發出的紋理「工作線程」「混合」。線程2執行離屏渲染(渲染爲紋理),然後線程1將此紋理與其自己的紋理結合起來以生成顯示給用戶的幀。glReadPixels到EGLImage直接紋理比glReadPixels更慢比ByteBuffer和glTexSubImage2D?
我有一個工作解決方案,但我知道它效率低下,我正試圖改進它。在它的OnSurfaceCreated()方法中,線程1創建兩個紋理。線程2在它的繪製方法中,將glReadPixels()轉換爲ByteBuffer(讓我們將其稱爲bb)。線程2然後向線程1發信號通知一個新的幀已準備就緒,線程1調用glTexSubImage2D(bb)以使用來自線程2的新數據更新其紋理,並繼續進行「混合」以生成新的框架。
這種體系結構在某些Android設備上的效果比其他設備更好,我可以通過使用PBO獲得略微的性能提升。但我想通過使用所謂的「直接紋理」通過EGL圖像擴展(https://software.intel.com/en-us/articles/using-opengl-es-to-accelerate-apps-with-legacy-2d-guis),我可以通過消除昂貴的glTexSubImage2D()調用來獲得一些好處。是的,我仍然有glReadPixels()調用仍然困擾我,但至少我應該測量一些改進。事實上,至少在三星Galaxy Tab S(Mali T628 GPU)上我的新代碼顯着比以前慢!怎麼會這樣?
在新的代碼線程1使用gralloc實例化EGLImage對象,並進行到其綁定到質地:
// note gbuffer::create() is a wrapper around gralloc
buffer = gbuffer::create(width, height, gbuffer::FORMAT_RGBA_8888);
EGLClientBuffer anb = buffer->getNativeBuffer();
EGLImageKHR pEGLImage = _eglCreateImageKHR(eglGetCurrentDisplay(), EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, (EGLClientBuffer)anb, attrs);
glBindTexture(GL_TEXTURE_2D, texid); // texid from glGenTextures(...)
_glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, pEGLImage);
那麼線程2,在它的主循環做它的摘屏幕渲染到紋理的東西,並通過glReadPixels()將數據重新推回到線程1,目標地址作爲EGLImage後面的後備存儲:
void* vaddr = buffer->lock();
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, vaddr);
buffer->unlock();
這如何可以比glReadPixels()成字節緩衝區隨後glTexSubImage2D從上述ByteBuffer的慢?我也對替代技術感興趣,因爲我不僅限於OpenGL-ES 2.0,而且可以使用OpenGL-ES 3.0。我嘗試過FBO,但遇到了一些問題。
作爲對第一個答案的迴應,我決定刺探實施不同的方法。也就是說,共享線程1和線程2之間的紋理。雖然我沒有共享部分工作,但我確實有線程1的EGLContext傳遞給線程2的EGLContext,因此理論上線程2可以與線程共享紋理1.通過這些更改,並保留glReadPixels()和glTexSubImage2D()調用,該應用程序可以正常工作,但速度比以前慢得多。奇怪。
我發現的另一個奇怪之處是處理javax.microedition.khronos.egl.EGLContext和android.opengl.EGLContext之間的區別。GLSurfaceView公開接口方法setEGLContextFactory(),讓我通過線程1的EGLContext主題2,如下面的:
public Thread1SurfaceView extends GLSurfaceView {
public Thread1SurfaceView(Context context) {
super(context);
// here is how I pass Thread 1's EGLContext to Thread 2
setEGLContextFactory(new EGLContextFactory() {
@Override
public javax.microedition.khronos.egl.EGLContext createContext(
final javax.microedition.khronos.egl.EGL10 egl,
final javax.microedition.khronos.egl.EGLDisplay display,
final javax.microedition.khronos.egl.EGLConfig eglConfig) {
// Configure context for OpenGL ES 3.0.
int[] attrib_list = {EGL14.EGL_CONTEXT_CLIENT_VERSION, 3, EGL14.EGL_NONE};
javax.microedition.khronos.egl.EGLContext renderContext =
egl.eglCreateContextdisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
mThread2 = new Thread2(renderContext);
}
});
}
以前,我使用的東西出EGL14命名空間,但由於GLSurfaceView的接口顯然依賴於EGL10的東西,我必須改變線程2的實現。到處使用EGL14我用javax.microedition.khronos.egl.EGL10取代。然後我的着色器停止編譯,直到我將GLES3添加到屬性列表。現在,雖然比以前慢了(但接下來我將刪除對glReadPixels和glTexSubImage2D的調用)。
我的後續問題是,這是處理javax.microedition.khronos.egl。*與android.opengl。*問題的正確方法嗎?我可以將javax.microedition.khronos.egl.EGL10轉換爲android.opengl.EGL14,將javax.microedition.khronos.egl.EGLDisplay轉換爲android.opengl.EGLDisplay,並將javax.microedition.khronos.egl.EGLContext轉換爲android.opengl。 EGLContext?我現在所擁有的東西看起來很醜陋,並且感覺不對,雖然這個提議的演員也不坐正確。我錯過了什麼嗎?