阅读本文之前请先学会opengl的基本使用方法:LearnOpenGL CN (learnopengl-cn.github.io)。
在SurfaceFlinger中Layer的合成流程中,如果是gpu合成则会调用frameworks/native/libs/renderengine/gl/GLESRenderEngine.cpp的drawLayers方法:
status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
ANativeWindowBuffer* const buffer,
const bool useFramebufferCache, base::unique_fd&& bufferFence,
base::unique_fd* drawFence) {
......
for (auto const layer : layers) {
......
// We only want to do a special handling for rounded corners when having rounded corners
// is the only reason it needs to turn on blending, otherwise, we handle it like the
// usual way since it needs to turn on blending anyway.
else if (layer->geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
handleRoundedCorners(display, *layer, mesh);
} else {
drawMesh(mesh);
}
......
}
......
}
调用了GLESRenderEngine的handleRoundedCorners方法:
void GLESRenderEngine::handleRoundedCorners(const DisplaySettings& display,
const LayerSettings& layer, const Mesh& mesh) {
// We separate the layer into 3 parts essentially, such that we only turn on blending for the
// top rectangle and the bottom rectangle, and turn off blending for the middle rectangle.
FloatRect bounds = layer.geometry.roundedCornersCrop;
// Explicitly compute the transform from the clip rectangle to the physical
// display. Normally, this is done in glViewport but we explicitly compute
// it here so that we can get the scissor bounds correct.
const Rect& source = display.clip;
const Rect& destination = display.physicalDisplay;
// Here we compute the following transform:
// 1. Translate the top left corner of the source clip to (0, 0)
// 2. Rotate the clip rectangle about the origin in accordance with the
// orientation flag
// 3. Translate the top left corner back to the origin.
// 4. Scale the clip rectangle to the destination rectangle dimensions
// 5. Translate the top left corner to the destination rectangle's top left
// corner.
const mat4 translateSource = mat4::translate(vec4(-source.left, -source.top, 0, 1));
mat4 rotation;
int displacementX = 0;
int displacementY = 0;
float destinationWidth = static_cast<float>(destination.getWidth());
float destinationHeight = static_cast<float>(destination.getHeight());
float sourceWidth = static_cast<float>(source.getWidth());
float sourceHeight = static_cast<float>(source.getHeight());
const float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f;
switch (display.orientation) {
case ui::Transform::ROT_90:
rotation = mat4::rotate(rot90InRadians, vec3(0, 0, 1));
displacementX = source.getHeight();
std::swap(sourceHeight, sourceWidth);
break;
case ui::Transform::ROT_180:
rotation = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1));
displacementY = source.getHeight();
displacementX = source.getWidth();
break;
case ui::Transform::ROT_270:
rotation = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1));
displacementY = source.getWidth();
std::swap(sourceHeight, sourceWidth);
break;
default:
break;
}
const mat4 intermediateTranslation = mat4::translate(vec4(displacementX, displacementY, 0, 1));
const mat4 scale = mat4::scale(
vec4(destinationWidth / sourceWidth, destinationHeight / sourceHeight, 1, 1));
const mat4 translateDestination =
mat4::translate(vec4(destination.left, destination.top, 0, 1));
const mat4 globalTransform =
translateDestination * scale * intermediateTranslation * rotation * translateSource;
const mat4 transformMatrix = globalTransform * layer.geometry.positionTransform;
const vec4 leftTopCoordinate(bounds.left, bounds.top, 1.0, 1.0);
const vec4 rightBottomCoordinate(bounds.right, bounds.bottom, 1.0, 1.0);
const vec4 leftTopCoordinateInBuffer = transformMatrix * leftTopCoordinate;
const vec4 rightBottomCoordinateInBuffer = transformMatrix * rightBottomCoordinate;
bounds = FloatRect(std::min(leftTopCoordinateInBuffer[0], rightBottomCoordinateInBuffer[0]),
std::min(leftTopCoordinateInBuffer[1], rightBottomCoordinateInBuffer[1]),
std::max(leftTopCoordinateInBuffer[0], rightBottomCoordinateInBuffer[0]),
std::max(leftTopCoordinateInBuffer[1], rightBottomCoordinateInBuffer[1]));
// Finally, we cut the layer into 3 parts, with top and bottom parts having rounded corners
// and the middle part without rounded corners.
const int32_t radius = ceil(layer.geometry.roundedCornersRadius);
const Rect topRect(bounds.left, bounds.top, bounds.right, bounds.top + radius);
setScissor(topRect);
drawMesh(mesh);
const Rect bottomRect(bounds.left, bounds.bottom - radius, bounds.right, bounds.bottom);
setScissor(bottomRect);
drawMesh(mesh);
// The middle part of the layer can turn off blending.
if (topRect.bottom < bottomRect.top) {
const Rect middleRect(bounds.left, bounds.top + radius, bounds.right,
bounds.bottom - radius);
setScissor(middleRect);
mState.cornerRadius = 0.0;
disableBlending();
drawMesh(mesh);
}
disableScissor();
}
在这里将layer裁剪成了3部分,其中中间部分不做处理,上部分和下部分将会裁剪出圆角。最后调用了GLESRenderEngine的drawMesh方法:
void GLESRenderEngine::drawMesh(const Mesh& mesh) {
......
glVertexAttribPointer(Program::position, mesh.getVertexSize(), GL_FLOAT, GL_FALSE,
mesh.getByteStride(), mesh.getPositions());
if (mState.cornerRadius > 0.0f) {
glEnableVertexAttribArray(Program::cropCoords);
glVertexAttribPointer(Program::cropCoords, mesh.getVertexSize(), GL_FLOAT, GL_FALSE,
mesh.getByteStride(), mesh.getCropCoords());
}
......
ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
managedState);
if (mState.drawShadows) {
glDrawElements(mesh.getPrimitive(), mesh.getIndexCount(), GL_UNSIGNED_SHORT,
mesh.getIndices());
} else {
glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
}
......
if (mState.cornerRadius > 0.0f) {
glDisableVertexAttribArray(Program::cropCoords);
}
if (mState.drawShadows) {
glDisableVertexAttribArray(Program::shadowColor);
glDisableVertexAttribArray(Program::shadowParams);
}
}
调用了frameworks/native/libs/renderengine/gl/ProgramCache.cpp的useProgram方法:
void ProgramCache::useProgram(EGLContext context, const Description& description) {
// generate the key for the shader based on the description
Key needs(computeKey(description));
// look-up the program in the cache
auto& cache = mCaches[context];
auto it = cache.find(needs);
if (it == cache.end()) {
// we didn't find our program, so generate one...
nsecs_t time = systemTime();
it = cache.emplace(needs, generateProgram(needs)).first;
time = systemTime() - time;
ALOGV(">>> generated new program for context %p: needs=%08X, time=%u ms (%zu programs)",
context, needs.mKey, uint32_t(ns2ms(time)), cache.size());
}
// here we have a suitable program for this description
std::unique_ptr<Program>& program = it->second;
if (program->isValid()) {
program->use();
program->setUniforms(description);
}
}
这里通过ProgramCache的generateProgram方法加载shader:
std::unique_ptr<Program> ProgramCache::generateProgram(const Key& needs) {
ATRACE_CALL();
// vertex shader
String8 vs = generateVertexShader(needs);
// fragment shader
String8 fs = generateFragmentShader(needs);
return std::make_unique<Program>(needs, vs.string(), fs.string());
}
其中vertexShader将顶点的坐标准备好传给fragmentShader(这里cropCoords虽然不是顶点的坐标,但是通过打印log发现其与顶点坐标完全相同):
String8 ProgramCache::generateVertexShader(const Key& needs) {
......
if (needs.hasRoundedCorners()) {
vs << "attribute lowp vec4 cropCoords;";
vs << "varying lowp vec2 outCropCoords;";
}
......
if (needs.hasRoundedCorners()) {
vs << "outCropCoords = cropCoords.st;";
}
......
}
然后在fragmentShader里面根据圆角的半径计算将在圆角之外的部分的alpha设为0使之完全透明:
String8 ProgramCache::generateFragmentShader(const Key& needs) {
......
if (needs.hasRoundedCorners()) {
// Rounded corners implementation using a signed distance function.
fs << R"__SHADER__(
uniform float cornerRadius;
uniform vec2 cropCenter;
varying vec2 outCropCoords;
/**
* This function takes the current crop coordinates and calculates an alpha value based
* on the corner radius and distance from the crop center.
*/
float applyCornerRadius(vec2 cropCoords)
{
vec2 position = cropCoords - cropCenter;
// Scale down the dist vector here, as otherwise large corner
// radii can cause floating point issues when computing the norm
vec2 dist = (abs(position) - cropCenter + vec2(cornerRadius)) / 16.0;
// Once we've found the norm, then scale back up.
float plane = length(max(dist, vec2(0.0))) * 16.0;
return 1.0 - clamp(plane - cornerRadius, 0.0, 1.0);
}
)__SHADER__";
}
......
if (needs.hasRoundedCorners()) {
......
fs << "gl_FragColor.a *= applyCornerRadius(outCropCoords);";
......
}
......
}
其中dist是从圆角的圆的圆心到当前像素坐标cropCoords的矢量。
就这样在圆角的圆之外的区域的alpha被设为了0,从而使得矩形窗口的四个角是圆的。