本篇位置:模块一 · 开篇 | 前置:无 | 预计阅读:10 分钟
这是一个系列教程的开篇。它写给每天在 libgui / libhwui / SurfaceFlinger / libEGL / libvulkan 里打转,API 都会用,但数学基本忘光了的人。
标题里"够用"两个字是认真的:不追求把图形学数学讲完整,只讲读 AOSP 显示相关代码会撞上的那些。
本篇位置:模块一 · 开篇 | 前置:无 | 预计阅读:10 分钟
这是一个系列教程的开篇。它写给每天在 libgui / libhwui / SurfaceFlinger / libEGL / libvulkan 里打转,API 都会用,但数学基本忘光了的人。
标题里"够用"两个字是认真的:不追求把图形学数学讲完整,只讲读 AOSP 显示相关代码会撞上的那些。
本篇位置:模块二 · 第 1 篇 | 前置:模块一 · 图形学数学基础 | 预计阅读:25 分钟
SurfaceFlinger 回落 GPU 合成时,最后干活的是这个函数(libs/renderengine/skia/SkiaRenderEngine.cpp,删去了调试与厂商分支):
void SkiaRenderEngine::drawLayersInternal(...) {
// ...
for (const auto& layer : layers) {
// 图层自己的变换,作用到画布矩阵上
canvas->concat(getSkM44(layer.geometry.positionTransform).asM33());
const auto [bounds, roundRectClip] =
getBoundsAndClip(layer.geometry.boundaries, layer.geometry.roundedCornersCrop,
layer.geometry.roundedCornersRadii);
// ... 建 shader、设 paint ...
canvas->drawRect(bounds.rect(), paint);
}
auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface));
}
本篇位置:模块二 · 第 2 篇 | 前置:开篇 | 预计阅读:25 分钟
合成每个图层之前,这一行把图层自己的变换叠到画布上(libs/renderengine/skia/SkiaRenderEngine.cpp):
// Layers have a local transform that should be applied to them
canvas->concat(getSkM44(layer.geometry.positionTransform).asM33());
本篇位置:模块二 · 第 3 篇 | 前置:顶点 | 预计阅读:30 分钟
SurfaceFlinger 里有一个类,专门回答"哪些像素被盖住了"这个问题(libs/ui/include/ui/Region.h):
class Region : public LightFlattenable<Region> {
public:
bool contains(int x, int y) const;
Region& orSelf(const Region& rhs);
Region& andSelf(const Region& rhs);
Region& subtractSelf(const Region& rhs);
// ...
};
本篇位置:模块二 · 第 4 篇 | 前置:光栅化 | 预计阅读:30 分钟
给图层建 shader 时,纹理矩阵是这么拼出来的(libs/renderengine/skia/SkiaRenderEngine.cpp):
auto texMatrix = getSkM44(item.textureTransform).asM33();
// textureTransform was intended to be passed directly into a shader, so when
// building the total matrix with the textureTransform we need to first
// normalize it, then apply the textureTransform, then scale back up.
texMatrix.preScale(1.0f / bounds.width(), 1.0f / bounds.height());
texMatrix.postScale(image->width(), image->height());
本篇位置:模块二 · 第 5 篇 | 前置:纹理 | 预计阅读:30 分钟
hwui 处理 View 的 elevation 时,画之前先按 Z 排一次序(libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp):
void StartReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
// ...
std::stable_sort(mChildren.begin(), mChildren.end(),
[](RenderNodeDrawable* a, RenderNodeDrawable* b) {
const float aZValue = a->getNodeProperties().getZ();
const float bZValue = b->getNodeProperties().getZ();
return aZValue < bZValue;
});
// 然后按这个顺序依次画
}
本篇位置:模块二 · 第 6 篇 | 前置:深度 | 预计阅读:30 分钟
给图层建 SkImage 之前,要先声明它的 alpha 类型(libs/renderengine/skia/SkiaRenderEngine.cpp):
// isOpaque means we need to ignore the alpha in the image,
// replacing it with the alpha specified by the LayerSettings. See
// https://developer.android.com/reference/android/view/SurfaceControl.Builder#setOpaque(boolean)
// Signal this with kUnknown_SkAlphaType, since kOpaque_SkAlphaType is a promise that
// the image's contents are already opaque.
const auto alphaType = item.isOpaque ? kUnknown_SkAlphaType :
item.usePremultipliedAlpha ? kPremul_SkAlphaType
: kUnpremul_SkAlphaType;
本篇位置:模块二 · 第 7 篇 | 前置:混合 | 预计阅读:30 分钟
hwui 里有一盏灯。它是全局的、静态的,整个 Android UI 共用一盏(libs/hwui/Lighting.h):
struct LightGeometry {
Vector3 center;
float radius;
};
struct LightInfo {
LightInfo(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha)
: ambientShadowAlpha(ambientShadowAlpha), spotShadowAlpha(spotShadowAlpha) {}
uint8_t ambientShadowAlpha;
uint8_t spotShadowAlpha;
};
本篇位置:模块二 · 第 8 篇 | 前置:光照 | 预计阅读:30 分钟
上一篇那盏灯,用在这里(libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp,删去厂商定制):
void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, const RenderNode* casterNode,
const ShadowInfo& shadowInfo) {
const RenderProperties& casterProperties = casterNode->properties();
const SkPath* casterPath = shadowInfo.casterPath;
const Vector3 lightPos = LightingInfo::getLightCenter();
SkPoint3 skiaLightPos = SkPoint3::Make(lightPos.x, lightPos.y, lightPos.z);
SkPoint3 zParams = SkPoint3::Make(0, 0, casterProperties.getZ());
SkShadowUtils::DrawShadow(
canvas, *casterPath, zParams, skiaLightPos, LightingInfo::getLightRadius(),
shadowInfo.ambientColor, shadowInfo.spotColor,
shadowInfo.casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0);
}
本篇位置:模块二 · 第 9 篇 | 前置:阴影 | 预计阅读:30 分钟
RenderEngine 创建离屏渲染目标时,采样数写死成 1(libs/renderengine/skia/compat/GaneshGpuContext.cpp):
sk_sp<SkSurface> GaneshGpuContext::createRenderTarget(SkImageInfo imageInfo) {
constexpr int kSampleCount = 1; // enable AA
constexpr SkSurfaceProps* kProps = nullptr;
constexpr bool kMipmapped = false;
return SkSurfaces::RenderTarget(mGrContext.get(), skgpu::Budgeted::kNo, imageInfo, kSampleCount,
kTopLeft_GrSurfaceOrigin, kProps, kMipmapped,
mGrContext->supportsProtectedContent());
}