Android opengles深入渲染优化

这是一篇关于Android opengles深入优化的经验

1.如果有视频和图片结合一起的情况,例如滤镜遮罩等场景。opengles的oes扩展是将mediadecode解码硬解到对应的纹理空间当中。硬解解析出来的是yuv数据,通过oes转化为rgba数据。 oes的视频纹理,和正常的图片纹理,是能够同时运用在同一个shader当中的。但是网上资料都不会提及到,都会让通过Framebuffer做一层数据缓存作为转换。这样会浪费一次渲染。

scss 复制代码
//初始化视频纹理
shared_ptr<TextureInfo> TextureLoadUtil::loadVideoTexture() {
    //shader
    GLuint textureId;
    glGenTextures(1,&textureId);
    glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureId);
    glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    auto textureInfo = make_shared<TextureInfo>();
    textureInfo->textureId = textureId;
    return textureInfo;
}

//初始化图片纹理
shared_ptr<TextureInfo> TextureLoadUtil::loadTexture(unsigned char *bitmap, AndroidBitmapInfo* info) {
    if (bitmap == nullptr) {
        YMLOGE("loadTexture bitmap = null");
        return nullptr;
    }

    GLuint textureObjectIds = 0;
    glGenTextures(1, &textureObjectIds);
    if (textureObjectIds == 0) {
        YMLOGE("loadTexture textureObjectIds = 0");
        return nullptr;
    }

    glBindTexture(GL_TEXTURE_2D, textureObjectIds);
//    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
//    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    //纹理放大缩小使用线性插值
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    //超出的部份会重复纹理坐标的边缘,产生一种边缘被拉伸的效果,s/t相当于x/y轴坐标
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);

    GLint format = GL_RGB;
    if (info->format == ANDROID_BITMAP_FORMAT_RGB_565) { //RGB三通道,例如jpg格式
        format = GL_RGB;
    } else if (info->format == ANDROID_BITMAP_FORMAT_RGBA_8888) {  //RGBA四通道,例如png格式 android图片是ARGB排列,所以还是要做图片转换的
        format = GL_RGBA;
    }
    //将图片数据生成一个2D纹理
    glTexImage2D(GL_TEXTURE_2D, 0, format, info->width, info->height, 0, format, GL_UNSIGNED_BYTE,
                 bitmap);

    glGenerateMipmap(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, 0);
    auto textureInfo = make_shared<TextureInfo>();
    textureInfo->width = info->width;
    textureInfo->height = info->height;
    textureInfo->textureId = textureObjectIds;
    return textureInfo;
}

//glsl里面
uniform sampler2D u_texture;
uniform samplerExternalOES v_texture;
//这样可以选择性获取到纹理
color = u_playType ? texture(v_texture, v_texcoord): texture(u_texture, v_texcoord);

//这里需要注意,激活纹理的时候(glActiveTexture),不能用同一个坑位,不然会导致无法展示
glActiveTexture(GL_TEXTURE0);
if (!sdInfo->isVideo) {
    glBindTexture(GL_TEXTURE_2D, sdInfo->srcTextureId);
}
// 纹理坐标
glUniform1i(uTextureLocation, 0);
glActiveTexture(GL_TEXTURE2);
if (sdInfo->isVideo) {
    glBindTexture(GL_TEXTURE_EXTERNAL_OES, sdInfo->srcTextureId);
}
// 纹理坐标
glUniform1i(vTextureLocation, 2);

2.切换shader会有一定开销,如果不是shader逻辑非常多那么最好的做法,就是将shader的逻辑都做到同一个glsl当中。

那么当shader逻辑不断膨胀后,那么会出现问题呢?

会出现逻辑的分支会变得非常多,逻辑分支变多,那么整个shader运动的速度就会越来越慢

之后因为shader代码变多了,会被编译到gpu当中,会让gpu的占用不断上升。那听起来将会是灾难级的。

如何可以解决这种问题呢?

Android当中,glsl文件是可以通过字节拼接的,编译前就条件判断来选择必须加入到glsl字符串内逻辑。

python 复制代码
string fs = R"(#version 300 es)";
fs.append("\n");
if (android_get_device_api_level() <= 23) {
    fs.append("#extension GL_OES_EGL_image_external : require");
} else {
    fs.append("#extension GL_OES_EGL_image_external_essl3 : require");
}
fs.append("\n");
fs.append(R"(
    precision mediump float;
    uniform sampler2D u_texture;
    uniform sampler2D u_dstTexture;
    uniform samplerExternalOES v_texture;
    )
);

3.framebuffer的创建是需要创建耗时的,在渲染当中去创建framebuffer不断的申请和销毁gpu空间,并不是好事。

良好的习惯,我们一定要提前创建好必须的framebuffer。

如果想要进一步利用好,我们可以对framebuffer创建缓存池或者通过交换framebuffer的指针地址的方法来,减少framebuffer的创建

arduino 复制代码
frambuffer1.swap(framebuffer2);

framebuffer重新绑定后,可以进行纹理的viewport大小调整,以及是否清屏Framebuffer的操作。

4.如果确定计算矩阵的数据没有变化,我们可以选择性维持Framebuffer的纹理数据。

当然有一些特殊的方式不要进行维持纹理,如视频解码的纹理,一定不要维持framebuffer数据不清空,不然不会更新到视频数据。

5.Android硬解h264的mp4,mp4的长宽尺寸需要整取8,不然会出现显示位置偏移的情况。h265测试的时候,某些资源不会出现这种情况。不知道是否机型还是h265解码有优化。

6.计算加速,可以考虑使用arm_neon.h库进行计算加速,特别multipy这种计算,可以考虑使用

ini 复制代码
void Matrix::multiply(const Matrix &a, const Matrix &b, Matrix &dst) {
    Matrix::Matrix() {
        values[0] = 1;
        values[1] = 0;
        values[2] = 0;
        values[3] = 0;
        values[4] = 0;
        values[5] = 1;
        values[6] = 0;
        values[7] = 0;
        values[8] = 0;
        values[9] = 0;
        values[10] = 1;
        values[11] = 0;
        values[12] = 0;
        values[13] = 0;
        values[14] = 0;
        values[15] = 1;
    }

    const float* a_data = &a.values[0];
    const float* b_data = &b.values[0];
    float* dst_data = &dst[0];

    // 加载a的四个列向量
    float32x4x4_t a_cols = vld4q_f32(a_data);

    // 加载b的四行
    float32x4_t b_row0 = vld1q_f32(b_data);
    float32x4_t b_row1 = vld1q_f32(b_data + 4);
    float32x4_t b_row2 = vld1q_f32(b_data + 8);
    float32x4_t b_row3 = vld1q_f32(b_data + 12);

    // 计算dst的四行
    computeRow(a_cols,b_row0, dst_data);
    computeRow(a_cols, b_row1, dst_data + 4);
    computeRow(a_cols, b_row2, dst_data + 8);
    computeRow(a_cols, b_row3, dst_data + 12);
}


void Matrix::computeRow(float32x4x4_t a_cols, float32x4_t b_row, float *dst_row) {
    float32x4_t dot0 = vmulq_f32(b_row, a_cols.val[0]);
    float32x2_t sum0 = vpadd_f32(vget_high_f32(dot0), vget_low_f32(dot0));
    sum0 = vpadd_f32(sum0, sum0);
    dst_row[0] = vget_lane_f32(sum0, 0);

    float32x4_t dot1 = vmulq_f32(b_row, a_cols.val[1]);
    float32x2_t sum1 = vpadd_f32(vget_high_f32(dot1), vget_low_f32(dot1));
    sum1 = vpadd_f32(sum1, sum1);
    dst_row[1] = vget_lane_f32(sum1, 0);

    float32x4_t dot2 = vmulq_f32(b_row, a_cols.val[2]);
    float32x2_t sum2 = vpadd_f32(vget_high_f32(dot2), vget_low_f32(dot2));
    sum2 = vpadd_f32(sum2, sum2);
    dst_row[2] = vget_lane_f32(sum2, 0);

    float32x4_t dot3 = vmulq_f32(b_row, a_cols.val[3]);
    float32x2_t sum3 = vpadd_f32(vget_high_f32(dot3), vget_low_f32(dot3));
    sum3 = vpadd_f32(sum3, sum3);
    dst_row[3] = vget_lane_f32(sum3, 0);
}

计算速度是否有提升,还是需要看适配场景的,有可能并不会出现提升。

7.glsl减少if语句和判断,有些编译器会对三元运算符直接优化

相关推荐
扶尔魔ocy6 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行并进行态势标绘(四)
qt·opengl·osg
:mnong7 天前
Web 三维可视化技术对比:ParaView Web vs 本项目 wasm + Three.js
前端·webgl·opengl·opencl·科学可视化
扶尔魔ocy9 天前
【qt openGL入门笔记】OSG实现显示飞机模型飞行的点迹(二)
qt·opengl·osg
扶尔魔ocy9 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行(三)
opengl·osg
扶尔魔ocy10 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行
qt·opengl·osg
扶尔魔ocy14 天前
【qt openGL入门笔记】FPS相机
qt·opengl
扶尔魔ocy14 天前
qt openGL入门笔记】FPS相机和轨道相机
qt·opengl
扶尔魔ocy15 天前
【qt openGL入门笔记】基础光照
qt·opengl
扶尔魔ocy16 天前
【qt openGL入门笔记】坐标系统和摄像机
qt·opengl
茉莉玫瑰花茶19 天前
OpenGL [ 纹理 ]
开发语言·c++·opengl