#力扣 LeetCode劍指 Offer 57. 和為s的兩個數字 @FDDLC
阿新 • • 發佈:2021-01-25
我們知道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 變數
- texCoords 紋理座標
- position 頂點座標
3個uniform 變數
- projection 頂點投影陣
- texture 紋理座標歸一化陣
- 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(<, 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);
}
}
主要涉及兩個方面:
- 頂點,紋理座標設定
- 統一變數設定
統一變數設定程式碼如下:
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簡單介紹。