OpenGL ES 高級進階:fence同步

你們好,這是個人OpenGL ES 高級進階系列文章,在個人github上有一個與本系列文章對應的項目,歡迎關注,連接:github.com/kenneycode/…git

今天給你們介紹OpenGL ES 3.0的一個特性fence,它能夠用來同步OpenGL命令,在多線程編程時頗有用,我以前的一篇文章《OpenGL ES 命令隊列及glFlush/glFinish》中有說到,OpenGL命令的執行是在GPU上的,咱們調用OpenGL方法其實是往OpenGL的命令隊列裏面插入命令,GPU從命令中取出命令執行。github

所以,通常狀況下咱們調用OpenGL方法後,並非立刻有效果的,若是要某處確保以前的OpenGL執行完,就須要用到glFinish,但glFinish只能保證本線程對應的命令隊列中的命令執行完,這就意味着不能在一個線程中等待另外一個線程的OpenGL命令執行完,這就有很大的限制,回想咱們在CPU上的同步操做,例如咱們在一個線程中wait,在另外一個線程中notify,這很容易實如今一個線程中等待另外一個線程的指定任務執行完成,這也是咱們很經常使用的操做,但在對於GPU,沒法用glFinish來實現相似的邏輯,實際上,在OpenGL ES 3.0之前,是沒法實現的。編程

到了OpenGL ES 3.0,咱們能夠用fence實現,使用越來也很簡單,就是在一個線程中插入一個fence,而後在另外一個線程中就能夠去等待這個fence,我畫了一張圖:多線程

例如咱們有這樣一種邏輯,在GLThread 0中渲染一個紋理,在另外一個線程GLThread 1中將這個紋理拿去使用,那就須要確保在GLThread 1使用這個紋理時,GLThread 0對這個紋理的渲染已經完成,有了fence後,咱們能夠在GLThread 0渲染操做以後插入一個fence,而後在GLThread 1要使用這個紋理時去等這個fenceide

咱們來看看代碼,先看看插入fence的代碼:post

val fenceSyncObject = GLES30.glFenceSync(GLES30.GL_SYNC_GPU_COMMANDS_COMPLETE, 0)
複製代碼

這個方法調用後會往當前線程的命令隊列中插入一個fence並返回一個long型變量來代碼這個fence同步對象,以便於其它地方去等待它,有2個方法能夠用於等待,glWaitSyncglClientWaitSync,它們的差異是glWaitSync是在GPU上等待,glClientWaitSync是在CPU上等待。spa

來看看咱們有例子代碼,在這個例子中,咱們在一個線程中渲染一張圖到一個紋理同時到屏幕上,在另外一個線程中將這個紋理讀出來顯示到屏幕的右下角的一個ImageView上:線程

override fun onDrawFrame(gl: GL10?) {
            
    ...
    
    // 渲染到紋理上
    // Render to texture
    GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, textureCoordinateData.size / VERTEX_COMPONENT_COUNT)

    // 向OpenGL的Command Buffer中插入一個fence
    // Insert a fence into the OpenGL command buffer
    val fenceSyncObject = GLES30.glFenceSync(GLES30.GL_SYNC_GPU_COMMANDS_COMPLETE, 0)

    // 在另外一個線程中讀取當前線程的渲染結果
    // Read the render result in the other thread
    otherThreadHandler.post {
        if (!flag) {

            // 等待fence前的OpenGL命令執行完畢
            // Waiting for completion of the OpenGL commands before our fence
            GLES30.glWaitSync(fenceSyncObject, 0, GLES30.GL_TIMEOUT_IGNORED)

            // 刪除fence同步對象
            // Delete the fence sync object
            GLES30.glDeleteSync(fenceSyncObject)

            val frameBuffers = IntArray(1)
            GLES30.glGenFramebuffers(frameBuffers.size, frameBuffers, 0)
            GLES30.glActiveTexture(GLES30.GL_TEXTURE1)
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, sharedTexture)
            GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, frameBuffers[0])
            GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, sharedTexture, 0)
            val buffer = ByteBuffer.wrap(ByteArray(glSurfaceViewWidth * glSurfaceViewHeight * 4))
            GLES30.glReadPixels(0, 0, glSurfaceViewWidth, glSurfaceViewHeight, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, buffer)
            GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
            val bitmap = Bitmap.createBitmap(glSurfaceViewWidth, glSurfaceViewHeight, Bitmap.Config.ARGB_8888)
            buffer.position(0)
            bitmap.copyPixelsFromBuffer(buffer)
            flag = true

            // 將讀取到的渲染結果顯示到一個ImageView上
            // Display the read render result on a ImageView
            imageView.post {
                imageView.setImageBitmap(bitmap)
            }
        }

    }

    // 將frame buffer綁回0號,將渲染結果同時也顯示到屏幕上
    // Bind frame buffer to 0# and also render the result on screen
    GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)

    GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
    GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, imageTexture)

    GLES30.glClearColor(0.9f, 0.9f, 0.9f, 1f)

    // 清屏
    // Clear the screen
    GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)

    // 設置視口,這裏設置爲整個GLSurfaceView區域
    // Set the viewport to the full GLSurfaceView
    GLES30.glViewport(0, 0, glSurfaceViewWidth, glSurfaceViewHeight)

    GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, vertexData.size / VERTEX_COMPONENT_COUNT)
}
複製代碼

加了fence後就能保證在另外一個線程中讀出的時候,圖片紋理已經渲染完成了,若是不加fence,可能會觀察到讀出來的紋理是殘缺不全的,渲染操做越是複雜,越是可能觀察到,由於讀的時候還來不及渲染完,在這個例子中,我作了一個模糊操做,讓渲染操做稍微變得複雜。code

下面是這個例子的效果:cdn

代碼在我githubOpenGLESPro項目中,本文對應的是SampleFenceSync,項目連接:github.com/kenneycode/…

感謝閱讀!

相關文章
相關標籤/搜索