• android opengles从帧缓存高效拷贝数据


    1.glReadPixels直接拷贝到CPU

    public int[] cutRectToBmpDataPure(RectF drawRect) {
    			IntBuffer ib;
            RectF rectF = new RectF(drawRect.left, drawRect.top, drawRect.right, drawRect.bottom);
            //android UI坐标和GL坐标的反转
            float temp1 = rectF.top;
            rectF.top = rectF.bottom;
            rectF.bottom = temp1;
            int rectWidth = (int) Math.abs(rectF.right - rectF.left);
            int rectHeight = (int) Math.abs(rectF.top - rectF.bottom);
            if (preWidth != rectWidth || preHeight != rectHeight) {
                ib = IntBuffer.allocate(rectWidth * rectHeight);
            }
            preWidth = rectWidth;
            preHeight = rectHeight;
            GLES30.glReadPixels((int) rectF.left, (int) rectF.bottom, rectWidth, rectHeight, GL_RGBA, GL_UNSIGNED_BYTE,
                    ib);
            return ib.array();
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    2.使用pbo内存映射方式(貌似pbo不是这么用的,效率和方式1相当)

        public int[] DownloadPixels(RectF drawRect) {
        		IntBuffer ib;
            RectF rectF = new RectF(drawRect.left, drawRect.top, drawRect.right, drawRect.bottom);
            //android UI坐标和GL坐标的反转
            float temp1 = rectF.top;
            rectF.top = rectF.bottom;
            rectF.bottom = temp1;
            int rectWidth = (int) Math.abs(rectF.right - rectF.left);
            int rectHeight = (int) Math.abs(rectF.top - rectF.bottom);
            if (preWidth != rectWidth || preHeight != rectHeight) {
                ib = IntBuffer.allocate(rectWidth * rectHeight);
            }
            preWidth = rectWidth;
            preHeight = rectHeight;
    
            int[] m_DownloadPboIds= new int[]{GLES30.GL_NONE};
            int dataSize = (int) preWidth * (int) preHeight * 4;
            GLES30.glGenBuffers(1, m_DownloadPboIds,0);
            GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, m_DownloadPboIds[0]);
            GLES30.glBufferData(GLES30.GL_PIXEL_PACK_BUFFER, dataSize, null, GLES30.GL_STREAM_READ);
            GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, 0);
    
            GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, m_DownloadPboIds[0]);
            GLES30.glReadPixels((int) rectF.left, (int) rectF.bottom, rectWidth, rectHeight, GL_RGBA, GL_UNSIGNED_BYTE, 0);
    
            GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, m_DownloadPboIds[0]);
            Buffer mappedBuffer = GLES30.glMapBufferRange(GLES30.GL_PIXEL_PACK_BUFFER, 0, dataSize, GLES30.GL_MAP_READ_BIT);
            ByteBuffer byteBuf = (ByteBuffer)mappedBuffer;
            byteBuf.order(ByteOrder.nativeOrder());
            IntBuffer floatBuf = byteBuf.asIntBuffer();
            ib.clear();
            ib.put(floatBuf);
            GLES30.glUnmapBuffer(GLES30.GL_PIXEL_PACK_BUFFER);
            GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, 0);
            GLES30.glDeleteBuffers(1, m_DownloadPboIds,0);
            return ib.array();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    3.顶点缓冲内存映射方式

    在这里插入图片描述

    (1)创建顶点缓存

    glGenBuffers(1, &readBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, readBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * FRAME_WIDTH * FRAME_HEIGHT * 3, NULL, GL_DYNAMIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    
    • 1
    • 2
    • 3
    • 4

    (2)把帧缓存数据读取到顶点缓冲readBuffer

    //缓存绑定
    glBindBuffer(GL_PIXEL_PACK_BUFFER, readBuffer);
    //输出到readBuffer上
    glReadPixels(0, 0, FRAME_WIDTH, FRAME_HEIGHT, GL_RGB, GL_FLOAT, NULL);
    
    • 1
    • 2
    • 3
    • 4

    (3)把readBuffer中的数据映射到内存中

    //映射
    float *data = (float *)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
    //完成后解除映射
    glUnmapBuffer(GL_PIXEL_PACK_BUFFER); 
    
    • 1
    • 2
    • 3
    • 4

    代码(实际跟方式2一样,用法不对)

        public int[] DownloadPixels2(RectF drawRect) {
            RectF rectF = new RectF(drawRect.left, drawRect.top, drawRect.right, drawRect.bottom);
            //android UI坐标和GL坐标的反转
            float temp1 = rectF.top;
            rectF.top = rectF.bottom;
            rectF.bottom = temp1;
            int rectWidth = (int) Math.abs(rectF.right - rectF.left);
            int rectHeight = (int) Math.abs(rectF.top - rectF.bottom);
            if (preWidth != rectWidth || preHeight != rectHeight) {
                ib = IntBuffer.allocate(rectWidth * rectHeight);
            }
            preWidth = rectWidth;
            preHeight = rectHeight;
    
            int dataSize = (int) rectWidth * (int) rectHeight * 4;
            int[] readBuffer=new int[1];
            GLES30.glGenBuffers(1, readBuffer,0);
            GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, readBuffer[0]);
            GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, dataSize, null, GLES30.GL_DYNAMIC_DRAW);
            GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
            GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, readBuffer[0]);
            GLES30.glReadPixels((int) rectF.left, (int) rectF.bottom, rectWidth, rectHeight, GL_RGBA, GLES30.GL_UNSIGNED_BYTE, 0);
            //GLES32.glMapBuffer(GLES30.GL_PIXEL_PACK_BUFFER);  //这个接口不支持,用下面的代替
            Buffer mappedBuffer = GLES30.glMapBufferRange(GLES30.GL_PIXEL_PACK_BUFFER, 0, dataSize, GLES30.GL_MAP_READ_BIT);
            ByteBuffer byteBuf = (ByteBuffer)mappedBuffer;
            byteBuf.order(ByteOrder.nativeOrder());
            IntBuffer floatBuf = byteBuf.asIntBuffer();
            ib.clear();
            ib.put(floatBuf);
            GLES30.glUnmapBuffer(GLES30.GL_PIXEL_PACK_BUFFER);
            GLES30.glDeleteBuffers(1, readBuffer,0);
            return ib.array();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    最终改成异步读取缓存方案

    封装为工具

    
    public class GlBufferUtils {
        private int[] m_DownloadPboIds= new int[]{GLES30.GL_NONE};
        public Buffer m_mappedBuffer=null;
        public int m_width;
        public int m_height;
    
        public GlBufferUtils(int left, int bottom,int width,int height){
            m_width=width;
            m_height=height;
            int dataSize = width *  height * 4;
            GLES30.glGenBuffers(1, m_DownloadPboIds,0);
            GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, m_DownloadPboIds[0]);
            GLES30.glBufferData(GLES30.GL_PIXEL_PACK_BUFFER, dataSize, null, GLES30.GL_STREAM_READ);
            GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, 0);
    
            GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, m_DownloadPboIds[0]);
            GLES30.glReadPixels( left,  bottom, width, height, GL_RGBA, GL_UNSIGNED_BYTE, 0);
    
            GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, m_DownloadPboIds[0]);
            m_mappedBuffer = GLES30.glMapBufferRange(GLES30.GL_PIXEL_PACK_BUFFER, 0, dataSize, GLES30.GL_MAP_READ_BIT);
            GLES30.glUnmapBuffer(GLES30.GL_PIXEL_PACK_BUFFER);
            GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, 0);
        }
        public Buffer getBuffer(){
            return m_mappedBuffer;
        }
        public int[] getIntArray(){
            IntBuffer ib = IntBuffer.allocate(m_width * m_height);
            ByteBuffer byteBuf = (ByteBuffer)m_mappedBuffer;
            byteBuf.order(ByteOrder.nativeOrder());
            IntBuffer floatBuf = byteBuf.asIntBuffer();
            ib.clear();
            ib.put(floatBuf);
            return ib.array();
        }
    
        public void destory(){
            GLES30.glDeleteBuffers(1, m_DownloadPboIds,0);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    调用

        public GlBufferUtils readPixelsSync(RectF drawRect) {
            RectF rectF = new RectF(drawRect.left, drawRect.top, drawRect.right, drawRect.bottom);
            //android UI坐标和GL坐标的反转
            float temp1 = rectF.top;
            rectF.top = rectF.bottom;
            rectF.bottom = temp1;
            int rectWidth = (int) Math.abs(rectF.right - rectF.left);
            int rectHeight = (int) Math.abs(rectF.top - rectF.bottom);
            GlBufferUtils mgBufferMap=new GlBufferUtils((int) rectF.left, (int) rectF.bottom, rectWidth, rectHeight);
            return mgBufferMap;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    然后在另一个线程读取像素数据

    int[] data=glBufferUtils.getIntArray();
    ...
    //读取完数据删除gl缓存
    glBufferUtils.destory();
    
    • 1
    • 2
    • 3
    • 4

    https://blog.csdn.net/Kennethdroid/article/details/103931627
    https://blog.csdn.net/niu2212035673/article/details/80251949

  • 相关阅读:
    NIO-Socket实现简易聊天室
    C#:实现将整数转换为二进制表示形式的字符串算法(附完整源码)
    利用图神经网络进行药物再利用的计算方法(下)
    MySQL事务,从redo log、bin log、undo log说起...
    【极力推荐】Java基础300集
    wpf资源Resources探究性学习(一)
    IDEA插件开发(13)---Dynamic Plugins
    葡萄糖-聚乙二醇-四嗪/叶酸/多巴胺 Glucose-PEG-TZ/FA/Dopamine
    前端 JS 实现图片元素转 BASE64 编码
    react脚手架初始化项目及ts的应用(react+ts)
  • 原文地址:https://blog.csdn.net/qq_40500571/article/details/126136972