1. 程式人生 > 其它 >#力扣 LeetCode劍指 Offer 57. 和為s的兩個數字 @FDDLC

#力扣 LeetCode劍指 Offer 57. 和為s的兩個數字 @FDDLC

我們知道Android 是在SurfaceFlinger中將不同應用對應的Layer合成到DisplayDevice的FramebufferSurface上然後再提交給HWC進行顯示的(除overlay外,overlay是直接提交給HWC進行顯示的),這個過程被稱為 GLES composition,其詳細流程我們就介紹了,我們直接介紹下GLES composition使用的GLES Shader,從Shader上來了解下GLES composition的流程

1 GLES composition shader建立程式碼如下:

1.1 頂點著色器生成函式

//frameworks\native\services\surfaceflinger\RenderEngine\ProgramCache.cpp
String8 ProgramCache::generateVertexShader(const Key& needs) { Formatter vs; if (needs.isTexturing()) { vs << "attribute vec4 texCoords;" << "varying vec2 outTexCoords;"; } vs << "attribute vec4 position;" <<
"uniform mat4 projection;" << "uniform mat4 texture;" << "void main(void) {" << indent << "gl_Position = projection * position;"; if (needs.isTexturing()) { vs << "outTexCoords = (texture * texCoords).st;"
; } vs << dedent << "}"; return vs.getString(); }

2.2 片段著色器生成函式

//frameworks\native\services\surfaceflinger\RenderEngine\ProgramCache.cpp
String8 ProgramCache::generateFragmentShader(const Key& needs) {
    Formatter fs;
    if (needs.getTextureTarget() == Key::TEXTURE_EXT) {
        fs << "#extension GL_OES_EGL_image_external : require";
    }
    // default precision is required-ish in fragment shaders
    fs << "precision mediump float;";

    if (needs.getTextureTarget() == Key::TEXTURE_EXT) {
        fs << "uniform samplerExternalOES sampler;"
           << "varying vec2 outTexCoords;";
    } else if (needs.getTextureTarget() == Key::TEXTURE_2D) {
        fs << "uniform sampler2D sampler;"
           << "varying vec2 outTexCoords;";
    } else if (needs.getTextureTarget() == Key::TEXTURE_OFF) {
        fs << "uniform vec4 color;";
    }
    if (needs.hasPlaneAlpha()) {
        fs << "uniform float alphaPlane;";
    }
    if (needs.hasColorMatrix()) {
        fs << "uniform mat4 colorMatrix;";
    }
    if (needs.hasColorMatrix()) {
        ...
    }
    fs << "void main(void) {" << indent;
    if (needs.isTexturing()) {
        fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
    } else {
        fs << "gl_FragColor = color;";
    }
    if (needs.isOpaque()) {
        fs << "gl_FragColor.a = 1.0;";
    }
    if (needs.hasPlaneAlpha()) {
        // modulate the alpha value with planeAlpha
        if (needs.isPremultiplied()) {
            // ... and the color too if we're premultiplied
            fs << "gl_FragColor *= alphaPlane;";
        } else {
            fs << "gl_FragColor.a *= alphaPlane;";
        }
    }

    if (needs.hasColorMatrix()) {
		...
    }

    fs << dedent << "}";
    return fs.getString();
}

1.3 shader簡化

程式碼比較複雜,我們對其簡化下,目前比較常用的一組選項如下:

isTexturing =true
getTextureTarget = Key::TEXTURE_EXT
hasPlaneAlpha  =false
hasColorMatrix =false
"attribute vec4 texCoords;"
"varying vec2 outTexCoords;";
"attribute vec4 position;"
"uniform mat4 projection;"
"uniform mat4 texture;"
"void main(void) {"
"    gl_Position = projection * position;";
"    outTexCoords = (texture * texCoords).st;";
"}";


"#extension GL_OES_EGL_image_external : require";
"precision mediump float;";
"uniform samplerExternalOES sampler;"
"varying vec2 outTexCoords;";
"void main(void) {"
"	gl_FragColor = texture2D(sampler, outTexCoords);";
"}";

簡化後的程式碼還是比較簡單的,

其有2 attribute 變數

  1. texCoords 紋理座標
  2. position 頂點座標

3個uniform 變數

  1. projection 頂點投影陣
  2. texture 紋理座標歸一化陣
  3. sampler 紋理

一個varying 變數 outTexCoords,用於頂點片段著色器傳遞資料

下邊我們來介紹下這些資料生成和設定

2. texCoords,position座標生成

void Layer::drawWithOpenGL(const sp<const DisplayDevice>& hw,
        bool useIdentityTransform) const {
    const State& s(getDrawingState());
    //計算頂點座標
    computeGeometry(hw, mMesh, useIdentityTransform);
    //計算渲染區域bounds
    const Rect bounds{computeBounds()}; // Rounds from FloatRect

    Transform t = getTransform();
    Rect win = bounds;
    if (!s.finalCrop.isEmpty()) {
        win = t.transform(win);
        if (!win.intersect(s.finalCrop, &win)) {
            win.clear();
        }
        win = t.inverse().transform(win);
        if (!win.intersect(bounds, &win)) {
            win.clear();
        }
    }
    //紋理座標歸一化到[0 1]範圍
    float left   = float(win.left)   / float(s.active.w);
    float top    = float(win.top)    / float(s.active.h);
    float right  = float(win.right)  / float(s.active.w);
    float bottom = float(win.bottom) / float(s.active.h);

    // TODO: we probably want to generate the texture coords with the mesh
    // here we assume that we only have 4 vertices
    Mesh::VertexArray<vec2> texCoords(mMesh.getTexCoordArray<vec2>());
    //生成紋理座標
    texCoords[0] = vec2(left, 1.0f - top);
    texCoords[1] = vec2(left, 1.0f - bottom);
    texCoords[2] = vec2(right, 1.0f - bottom);
    texCoords[3] = vec2(right, 1.0f - top);

    RenderEngine& engine(mFlinger->getRenderEngine());
    //設定blend mode
    engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(s), getAlpha());
    //gl draw call
    engine.drawMesh(mMesh);
    //關閉blend mode
    engine.disableBlending();
}

computeGeometry實現如下:

void Layer::computeGeometry(const sp<const DisplayDevice>& hw, Mesh& mesh,
        bool useIdentityTransform) const
{
    const Layer::State& s(getDrawingState());
    const Transform hwTransform(hw->getTransform());
    const uint32_t hw_h = hw->getHeight();
    FloatRect win = computeBounds();

    vec2 lt = vec2(win.left, win.top);
    vec2 lb = vec2(win.left, win.bottom);
    vec2 rb = vec2(win.right, win.bottom);
    vec2 rt = vec2(win.right, win.top);

    Transform layerTransform = getTransform();
    if (!useIdentityTransform) {
        lt = layerTransform.transform(lt);
        lb = layerTransform.transform(lb);
        rb = layerTransform.transform(rb);
        rt = layerTransform.transform(rt);
    }

    if (!s.finalCrop.isEmpty()) {
        boundPoint(&lt, s.finalCrop);
        boundPoint(&lb, s.finalCrop);
        boundPoint(&rb, s.finalCrop);
        boundPoint(&rt, s.finalCrop);
    }

    Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
    position[0] = hwTransform.transform(lt);
    position[1] = hwTransform.transform(lb);
    position[2] = hwTransform.transform(rb);
    position[3] = hwTransform.transform(rt);
    for (size_t i=0 ; i<4 ; i++) {
        position[i].y = hw_h - position[i].y;
    }
}

我們列印下獲取的頂點,紋理座標

[SurfaceView - com.xxx.xxx/com.xxx.xxx.MainActivity#1] drawWithOpenGL
//頂點座標列印
[SurfaceView - com.xxx.xxx/com.xxx.xxx.MainActivity#1] position[0][2160.000000,3840.000000]
[SurfaceView - com.xxx.xxx/com.xxx.xxx.MainActivity#1] position[1][0.000000,3840.000000]
[SurfaceView - com.xxx.xxx/com.xxx.xxx.MainActivity#1] position[2][0.000000,0.000000]
[SurfaceView - com.xxx.xxx/com.xxx.xxx.MainActivity#1] position[3][2160.000000,0.000000]
//紋理座標列印
[SurfaceView - com.xxx.xxx/com.xxx.xxx.MainActivity#1] texCoords[0][0.000000,1.000000]
[SurfaceView - com.xxx.xxx/com.xxx.xxx.MainActivity#1] texCoords[1][0.000000,0.000000]
[SurfaceView - com.xxx.xxx/com.xxx.xxx.MainActivity#1] texCoords[2][1.000000,0.000000]
[SurfaceView - com.xxx.xxx/com.xxx.xxx.MainActivity#1] texCoords[3][1.000000,1.000000]

3. projection 統一變數生成

//DisplayDevice.cpp
void DisplayDevice::setViewportAndProjection() const {
    size_t w = mDisplayWidth;
    size_t h = mDisplayHeight;
    Rect sourceCrop(0, 0, w, h);
    mFlinger->getRenderEngine().setViewportAndProjection(w, h, sourceCrop, h,
        false, Transform::ROT_0);
}
void GLES20RenderEngine::setViewportAndProjection(
        size_t vpw, size_t vph, Rect sourceCrop, size_t hwh, bool yswap,
        Transform::orientation_flags rotation) {

    size_t l = sourceCrop.left;
    size_t r = sourceCrop.right;

    // In GL, (0, 0) is the bottom-left corner, so flip y coordinates
    size_t t = hwh - sourceCrop.top;
    size_t b = hwh - sourceCrop.bottom;

    mat4 m;
    if (yswap) {
        m = mat4::ortho(l, r, t, b, 0, 1);
    } else {
        m = mat4::ortho(l, r, b, t, 0, 1);
    }
    // Apply custom rotation to the projection.
    float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f;
    switch (rotation) {
        case Transform::ROT_0:
            break;
        case Transform::ROT_90:
            m = mat4::rotate(rot90InRadians, vec3(0,0,1)) * m;
            break;
        ...
        default:
            break;
    }
   //設定view port
    glViewport(0, 0, vpw, vph);
    mState.setProjectionMatrix(m);
    mVpWidth = vpw;
    mVpHeight = vph;
}
[Built-in Screen] setViewportAndProjection [2160,3840]
setViewportAndProjection sourceCrop [0,2160,0,3840]
setViewportAndProjection [vpw x vph] [2160,3840]
setViewportAndProjection [l r t b] [0,2160,3840,0]
ProjectionMatrix [0.000926,0.000000,0.000000,0.000000]
ProjectionMatrix [0.000000,0.000521,0.000000,0.000000]
ProjectionMatrix [0.000000,0.000000,-2.000000,0.000000]
ProjectionMatrix [-1.000000,-1.000000,-1.000000,1.000000]

4. texture 和 sampler統一變數生成

程式碼如下:

/*
 * onDraw will draw the current layer onto the presentable buffer
 */
void Layer::onDraw(const sp<const DisplayDevice>& hw, const Region& clip,
        bool useIdentityTransform) const
{
    ALOGE("[%s] onDraw", mName.string());
    ...
    // Bind the current buffer to the GL texture, and wait for it to be
    // ready for us to draw into.
    //更新texture內容
    status_t err = mSurfaceFlingerConsumer->bindTextureImage();
    ...
    //檢查該Layer是否需要繪製
    bool blackOutLayer = isProtected() || (isSecure() && !hw->isSecure()) || isHDRLayer();

    RenderEngine& engine(mFlinger->getRenderEngine());
    //blackOutLayer 為false,需要繪製
    if (!blackOutLayer) {
        // TODO: we could be more subtle with isFixedSize()
        const bool useFiltering = getFiltering() || needsFiltering(hw) || isFixedSize();

        // Query the texture matrix given our current filtering mode.
        float textureMatrix[16];
        mSurfaceFlingerConsumer->setFilteringEnabled(useFiltering);
        //從mSurfaceFlingerConsumer中獲取textureMatrix
        mSurfaceFlingerConsumer->getTransformMatrix(textureMatrix);
        ...
        // Set things up for texturing.
        //設定texture的大小
        mTexture.setDimensions(mActiveBuffer->getWidth(), mActiveBuffer->getHeight());
        //設定texture 過濾模式
        mTexture.setFiltering(useFiltering);
        //設定textureMatrix
        mTexture.setMatrix(textureMatrix);
        //繫結textture,也就是設定sampler同一變數
        engine.setupLayerTexturing(mTexture);
    } else {
        engine.setupLayerBlackedOut();
    }
    drawWithOpenGL(hw, useIdentityTransform);
    engine.disableTexturing();
}

4.1 textureMatrix計算如下:

//GLConsumer.cpp
void GLConsumer::computeTransformMatrix(float outTransform[16],
        const sp<GraphicBuffer>& buf, const Rect& cropRect, uint32_t transform,
        bool filtering) {

    float xform[16];
    for (int i = 0; i < 16; i++) {
        xform[i] = mtxIdentity[i];
    }
    if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
        float result[16];
        mtxMul(result, xform, mtxFlipH);
        for (int i = 0; i < 16; i++) {
            xform[i] = result[i];
        }
    }
    ...
    float mtxBeforeFlipV[16];
    if (!cropRect.isEmpty()) {
        float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f;
        float bufferWidth = buf->getWidth();
        float bufferHeight = buf->getHeight();
        float shrinkAmount = 0.0f;
        ...
        // Only shrink the dimensions that are not the size of the buffer.
        //sx是x方向縮小多少
        //tx是y方向平移多少
        if (cropRect.width() < bufferWidth) {
            tx = (float(cropRect.left) + shrinkAmount) / bufferWidth;
            sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) /
                    bufferWidth;
        }
        if (cropRect.height() < bufferHeight) {
            ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) /
                    bufferHeight;
            sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) /
                    bufferHeight;
        }
        float crop[16] = {
            sx, 0, 0, 0,
            0, sy, 0, 0,
            0, 0, 1, 0,
            tx, ty, 0, 1,
        };
        mtxMul(mtxBeforeFlipV, crop, xform);
    } else {
        ...
    }

    // SurfaceFlinger expects the top of its window textures to be at a Y
    // coordinate of 0, so GLConsumer must behave the same way.  We don't
    // want to expose this to applications, however, so we must add an
    // additional vertical flip to the transform after all the other transforms.
    mtxMul(outTransform, mtxFlipV, mtxBeforeFlipV);
}

computeTransformMatrix的詳細計算我們就不介紹了,現在列印某個Layer的計算結果如下:

mTextureMatrixLoc [1.000000,0.000000,0.000000,0.000000]
mTextureMatrixLoc [0.000000,-1.000000,0.000000,0.000000]
mTextureMatrixLoc [0.000000,0.000000,1.000000,0.000000]
mTextureMatrixLoc [0.000000,1.000000,0.000000,1.000000]

4.2 繫結紋理

//GLES20RenderEngine.cpp
void GLES20RenderEngine::setupLayerTexturing(const Texture& texture) {
    GLuint target = texture.getTextureTarget();
    glBindTexture(target, texture.getTextureName());
    GLenum filter = GL_NEAREST;
    if (texture.getFiltering()) {
        filter = GL_LINEAR;
    }
    glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
    glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);

    mState.setTexture(texture);
}

至此完成了shader所需的所有資料準備工作,下邊便可以開始繪製,程式碼如下

5. 渲染繪製

void GLES20RenderEngine::drawMesh(const Mesh& mesh) {
    //設定紋理座標
    if (mesh.getTexCoordsSize()) {
        glEnableVertexAttribArray(Program::texCoords);
        glVertexAttribPointer(Program::texCoords,
                mesh.getTexCoordsSize(),
                GL_FLOAT, GL_FALSE,
                mesh.getByteStride(),
                mesh.getTexCoords());
    }
   //設定頂點座標
    glVertexAttribPointer(Program::position,
            mesh.getVertexSize(),
            GL_FLOAT, GL_FALSE,
            mesh.getByteStride(),
            mesh.getPositions());
    //啟用 gl program
    //設定projection和texture統一變數 
    ProgramCache::getInstance().useProgram(mState);
    //gl draw call
    glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
    if (mesh.getTexCoordsSize()) {
        glDisableVertexAttribArray(Program::texCoords);
    }
}

主要涉及兩個方面:

  1. 頂點,紋理座標設定
  2. 統一變數設定

統一變數設定程式碼如下:

void ProgramCache::useProgram(const Description& description) {
    // generate the key for the shader based on the description
    Key needs(computeKey(description));
     // look-up the program in the cache
    Program* program = mCache.valueFor(needs);
    if (program == NULL) {
        program = generateProgram(needs);
        mCache.add(needs, program);
    }
    // here we have a suitable program for this description
    if (program->isValid()) {
        program->use();
        //設定統一變數
        program->setUniforms(description);
    }
}
void Program::setUniforms(const Description& desc) {

    if (mSamplerLoc >= 0) {
        glUniform1i(mSamplerLoc, 0);
        glUniformMatrix4fv(mTextureMatrixLoc, 1, GL_FALSE, desc.mTexture.getMatrix().asArray());
    }
    if (mAlphaPlaneLoc >= 0) {
        glUniform1f(mAlphaPlaneLoc, desc.mPlaneAlpha);
    }
    if (mColorLoc >= 0) {
        glUniform4fv(mColorLoc, 1, desc.mColor);
    }
    if (mColorMatrixLoc >= 0) {
        glUniformMatrix4fv(mColorMatrixLoc, 1, GL_FALSE, desc.mColorMatrix.asArray());
    }
    // these uniforms are always present
    glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, desc.mProjectionMatrix.asArray());
}

至此完成了The GLES Shader used by SurfaceFlinger for GLES composition簡單介紹。