关于Surface请参考下面文章
SurfaceFlinger学习笔记(一)应用启动流程
SurfaceFlinger学习笔记(二)之Surface
SurfaceFlinger学习笔记(三)之SurfaceFlinger进程
SurfaceFlinger学习笔记(四)之HWC2
SurfaceFlinger学习笔记(五)之HWUI
SurfaceFlinger学习笔记(六)之View Layout Draw过程分析
SurfaceFlinger学习笔记(七)之SKIA
下面代码基于android T,下面以绘制本地图片为例,介绍绘制流程
整个demo为底部三个tab,在home页听不绘制一张图片
准备流程
- 第一步:SkiaOpenGLPipeline::getFrame
这里主要进行两步
- 调用queryBufferAge查询可用buffer,并执行dequeueBuffer流程
- 调用eglBeginFrame
* frameworks/base/libs/hwui/renderthread/EglManager.cpp
Frame EglManager::beginFrame(EGLSurface surface) {
LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE, "Tried to beginFrame on EGL_NO_SURFACE!");
makeCurrent(surface);
Frame frame;
frame.mSurface = surface;
eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, &frame.mWidth);
eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, &frame.mHeight);
frame.mBufferAge = queryBufferAge(surface);
eglBeginFrame(mEglDisplay, surface);
return frame;
}
EGLint EglManager::queryBufferAge(EGLSurface surface) {
switch (mSwapBehavior) {
case SwapBehavior::Discard:
return 0;
case SwapBehavior::Preserved:
return 1;
case SwapBehavior::BufferAge:
EGLint bufferAge;
eglQuerySurface(mEglDisplay, surface, EGL_BUFFER_AGE_EXT, &bufferAge);
return bufferAge;
}
return 0;
}
绘制流程
- 第二步:SkiaOpenGLPipeline::draw
SkiaOpenGLPipeline::draw
- 调用EglManager.damageFrame主要是部分更新参数的设置,前面我们也damage的区域就是前面Prepare时累加器累加出来的
- 调用renderFrame进行纹理创建,实现在基类SkiaPipeline::renderFrame中
- 调用flushAndSubmit,进行纹理绑定
* frameworks/base/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
const LightGeometry& lightGeometry,
LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds,
bool opaque, const LightInfo& lightInfo,
const std::vector<sp<RenderNode>>& renderNodes,
FrameInfoVisualizer* profiler) {
1. damageFrame
mEglManager.damageFrame(frame, dirty);
...
// 这里调用SkSurface::MakeFromBackendRenderTarget
sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(
mRenderThread.getGrContext(), backendRT, this->getSurfaceOrigin(), colorType,
mSurfaceColorSpace, &props));
LightingInfo::updateLighting(lightGeometry, lightInfo);
2. damageFrame
renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
SkMatrix::I());
...
{
ATRACE_NAME("flush commands");
3. damageFrame
surface->flushAndSubmit();
}
layerUpdateQueue->clear();
...
return {true, IRenderPipeline::DrawResult::kUnknownTime};
}
创建纹理流程:
创建纹理调用堆栈
#00 pc 00000000005b4234 /system/lib64/libhwui.so (GrGLGpu::createTexture(SkISize, GrGLFormat, unsigned int, GrRenderable, GrGLTextureParameters::SamplerOverriddenState*, int, GrProtected)+108)
#01 pc 00000000005b3d2c /system/lib64/libhwui.so (GrGLGpu::onCreateTexture(SkISize, GrBackendFormat const&, GrRenderable, int, SkBudgeted, GrProtected, int, unsigned int)+224)
#02 pc 0000000000543994 /system/lib64/libhwui.so (GrGpu::createTextureCommon(SkISize, GrBackendFormat const&, GrTextureType, GrRenderable, int, SkBudgeted, GrProtected, int, unsigned int)+256)
#03 pc 0000000000543cf0 /system/lib64/libhwui.so (GrGpu::createTexture(SkISize, GrBackendFormat const&, GrTextureType, GrRenderable, int, SkBudgeted, GrProtected, GrColorType, GrColorType, GrMipLevel const*, int)+464)
#04 pc 000000000055b220 /system/lib64/libhwui.so (GrResourceProvider::createTexture(SkISize, GrBackendFormat const&, GrTextureType, GrColorType, GrRenderable, int, SkBudgeted, GrMipmapped, GrProtected, GrMipLevel const*)+636)
#05 pc 000000000055c290 /system/lib64/libhwui.so (GrResourceProvider::createTexture(SkISize, GrBackendFormat const&, GrTextureType, GrColorType, GrRenderable, int, SkBudgeted, SkBackingFit, GrProtected, GrMipLevel const&)+148)
#06 pc 000000000054e168 /system/lib64/libhwui.so (std::__1::__function::__func<GrProxyProvider::createNonMippedProxyFromBitmap(SkBitmap const&, SkBackingFit, SkBudgeted)::$_0, std::__1::allocator<GrProxyProvider::createNonMippedProxyFromBitmap(SkBitmap const&, SkBackingFit, SkBudgeted)::$_0>, GrSurfaceProxy::LazyCallbackResult (GrResourceProvider*, GrSurfaceProxy::LazySurfaceDesc const&)>::operator()(GrResourceProvider*&&, GrSurfaceProxy::LazySurfaceDesc const&) (.14006f304cec4648659a8a056b977056)+136)
#07 pc 00000000005613b0 /system/lib64/libhwui.so (GrSurfaceProxyPriv::doLazyInstantiation(GrResourceProvider*)+180)
#08 pc 000000000054c5ec /system/lib64/libhwui.so (GrProxyProvider::createProxyFromBitmap(SkBitmap const&, GrMipmapped, SkBackingFit, SkBudgeted)+1276)
#09 pc 000000000056a6f8 /system/lib64/libhwui.so (make_bmp_proxy(GrProxyProvider*, SkBitmap const&, GrColorType, GrMipmapped, SkBackingFit, SkBudgeted)+160)
#10 pc 000000000056a218 /system/lib64/libhwui.so (GrMakeCachedBitmapProxyView(GrRecordingContext*, SkBitmap const&, GrMipmapped)+424)
#11 pc 00000000003fdeb4 /system/lib64/libhwui.so (SkImage_Raster::onAsView(GrRecordingContext*, GrMipmapped, GrImageTexGenPolicy) const+380)
#12 pc 00000000003fdfb8 /system/lib64/libhwui.so (SkImage_Raster::onAsFragmentProcessor(GrRecordingContext*, SkSamplingOptions, SkTileMode const*, SkMatrix const&, SkRect const*, SkRect const*) const+100)
#13 pc 00000000003f8360 /system/lib64/libhwui.so (SkImage_Base::asFragmentProcessor(GrRecordingContext*, SkSamplingOptions, SkTileMode const*, SkMatrix const&, SkRect const*, SkRect const*) const+148)
#14 pc 000000000064fb60 /system/lib64/libhwui.so ((anonymous namespace)::draw_image(GrRecordingContext*, skgpu::v1::SurfaceDrawContext*, GrClip const*, SkMatrixProvider const&, SkPaint const&, SkImage_Base const&, SkRect const&, SkRect const&, SkPoint const*, SkMatrix const&, GrAA, GrQuadAAFlags, SkCanvas::SrcRectConstraint, SkSamplingOptions, SkTileMode)+1680)
#15 pc 000000000065141c /system/lib64/libhwui.so (skgpu::v1::Device::drawImageQuad(SkImage const*, SkRect const*, SkRect const*, SkPoint const*, GrAA, GrQuadAAFlags, SkMatrix const*, SkSamplingOptions const&, SkPaint const&, SkCanvas::SrcRectConstraint)+3092)
#16 pc 000000000064ced4 /system/lib64/libhwui.so (skgpu::v1::Device::drawImageRect(SkImage const*, SkRect const*, SkRect const&, SkSamplingOptions const&, SkPaint const&, SkCanvas::SrcRectConstraint)+80)
#17 pc 00000000002ea6b8 /system/lib64/libhwui.so (SkCanvas::onDrawImageRect2(SkImage const*, SkRect const&, SkRect const&, SkSamplingOptions const&, SkPaint const*, SkCanvas::SrcRectConstraint)+292)
#18 pc 00000000002657bc /system/lib64/libhwui.so (android::uirenderer::VectorDrawable::Tree::draw(SkCanvas*, SkRect const&, SkPaint const&)+312)
#19 pc 0000000000253668 /system/lib64/libhwui.so (android::uirenderer::DisplayListData::draw(SkCanvas*) const+132)
#20 pc 000000000023bc78 /system/lib64/libhwui.so (android::uirenderer::skiapipeline::RenderNodeDrawable::drawContent(SkCanvas*) const+1756)
#21 pc 000000000023c3b4 /system/lib64/libhwui.so (android::uirenderer::skiapipeline::RenderNodeDrawable::forceDraw(SkCanvas*) const+292)
#22 pc 00000000003095cc /system/lib64/libhwui.so (SkDrawable::draw(SkCanvas*, SkMatrix const*)+120)
#23 pc 0000000000253668 /system/lib64/libhwui.so (android::uirenderer::DisplayListData::draw(SkCanvas*) const+132)
#24 pc 000000000023bc78 /system/lib64/libhwui.so (android::uirenderer::skiapipeline::RenderNodeDrawable::drawContent(SkCanvas*) const+1756)
#25 pc 000000000023c3b4 /system/lib64/libhwui.so (android::uirenderer::skiapipeline::RenderNodeDrawable::forceDraw(SkCanvas*) const+292)
#26 pc 00000000003095cc /system/lib64/libhwui.so (SkDrawable::draw(SkCanvas*, SkMatrix const*)+120)
#27 pc 0000000000253668 /system/lib64/libhwui.so (android::uirenderer::DisplayListData::draw(SkCanvas*) const+132)
#28 pc 000000000023bc78 /system/lib64/libhwui.so (android::uirenderer::skiapipeline::RenderNodeDrawable::drawContent(SkCanvas*) const+1756)
#29 pc 000000000023c3b4 /system/lib64/libhwui.so (android::uirenderer::skiapipeline::RenderNodeDrawable::forceDraw(SkCanvas*) const+292)
#30 pc 00000000003095cc /system/lib64/libhwui.so (SkDrawable::draw(SkCanvas*, SkMatrix const*)+120)
#63 pc 000000000027e9cc /system/lib64/libhwui.so (android::uirenderer::skiapipeline::SkiaPipeline::renderFrameImpl(SkRect const&, std::__1::vector<android::sp<android::uirenderer::RenderNode>, std::__1::allocator<android::sp<android::uirenderer::RenderNode> > > const&, bool, android::uirenderer::Rect const&, SkCanvas*, SkMatrix const&)+512)
#64 pc 000000000027e50c /system/lib64/libhwui.so (android::uirenderer::skiapipeline::SkiaPipeline::renderFrame(android::uirenderer::LayerUpdateQueue const&, SkRect const&, std::__1::vector<android::sp<android::uirenderer::RenderNode>, std::__1::allocator<android::sp<android::uirenderer::RenderNode> > > const&, bool, android::uirenderer::Rect const&, sk_sp<SkSurface>, SkMatrix const&)+656)
#65 pc 000000000027c268 /system/lib64/libhwui.so (android::uirenderer::skiapipeline::SkiaOpenGLPipeline::draw(android::uirenderer::renderthread::Frame const&, SkRect const&, SkRect const&, android::uirenderer::LightGeometry const&, android::uirenderer::LayerUpdateQueue*, android::uirenderer::Rect const&, bool, android::uirenderer::LightInfo const&, std::__1::vector<android::sp<android::uirenderer::RenderNode>, std::__1::allocator<android::sp<android::uirenderer::RenderNode> > > const&, android::uirenderer::FrameInfoVisualizer*)+520)
#66 pc 0000000000283974 /system/lib64/libhwui.so (android::uirenderer::renderthread::CanvasContext::draw()+1104)
#67 pc 00000000002866b4 /system/lib64/libhwui.so (std::__1::__function::__func<android::uirenderer::renderthread::DrawFrameTask::postAndWait()::$_0, std::__1::allocator<android::uirenderer::renderthread::DrawFrameTask::postAndWait()::$_0>, void ()>::operator()() (.c1671e787f244890c877724752face20)+904)
#68 pc 0000000000276090 /system/lib64/libhwui.so (android::uirenderer::WorkQueue::process()+588)
#69 pc 00000000002972c0 /system/lib64/libhwui.so (android::uirenderer::renderthread::RenderThread::threadLoop()+416)
#70 pc 0000000000013598 /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+424)
#71 pc 00000000000f5548 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+208)
#72 pc 000000000008ef3c /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+68)
SkiaOpenGLPipeline继承SkiaPipeline
frameworks/base/libs/hwui/pipeline/skia/SkiaPipeline.cpp
void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
const std::vector<sp<RenderNode>>& nodes, bool opaque,
const Rect& contentDrawBounds, sk_sp<SkSurface> surface,
const SkMatrix& preTransform) {
...
// Initialize the canvas for the current frame, that might be a recording canvas if SKP
// capture is enabled.
SkCanvas* canvas = tryCapture(surface.get(), nodes[0].get(), layers);
// draw all layers up front
renderLayersImpl(layers, opaque);
renderFrameImpl(clip, nodes, opaque, contentDrawBounds, canvas, preTransform);
endCapture(surface.get());
...
Properties::skpCaptureEnabled = previousSkpEnabled;
}
void SkiaPipeline::renderFrameImpl(const SkRect& clip,
const std::vector<sp<RenderNode>>& nodes, bool opaque,
const Rect& contentDrawBounds, SkCanvas* canvas,
const SkMatrix& preTransform) {
...
//它有多个渲染nodes,其布局如下:
// #0 -背景(内容+标题)
// #1 -内容(本地边界为(0,0),将被翻译和剪辑到背景)
// #2 -附加的覆盖节点
//通常看不到背景,因为它将完全被内容所覆盖。
//在调整大小时,它可能会部分可见。下面的渲染循环将根据内容裁剪背景,并绘制它的其余部分。然后,它将绘制裁剪到背景中的内容(因为这表明窗口正在缩小)。
//额外的节点将被绘制在顶部,没有特定的剪切语义。
//通常内容边界应该是mContentDrawBounds-然而,我们将移动它到固定的边缘,以给它一个更稳定的外观(目前)。
//如果没有内容边界,我们将忽略上面所述的分层,并从2开始。
const Rect backdrop = nodeBounds(*nodes[0]);
// 内容将填充渲染目标空间的边界(注意,内容节点边界可能较大)
Rect content(contentDrawBounds.getWidth(), contentDrawBounds.getHeight());
content.translate(backdrop.left, backdrop.top);
if (!content.contains(backdrop) && !nodes[0]->nothingToDraw()) {
// 内容不完全重叠背景,所以填充内容(右/下)
//注意:在未来,如果内容没有捕捉到背景的左/顶部,这可能还需要填充左/顶部。目前,2向上和自由形式的位置内容都在背景的上/左,所以这是没有必要的。
RenderNodeDrawable backdropNode(nodes[0].get(), canvas);
if (content.right < backdrop.right) {
//如果内容右侧不能覆盖背景,则绘制右侧的背景区域
SkAutoCanvasRestore acr(canvas, true);
canvas->clipRect(SkRect::MakeLTRB(content.right, backdrop.top, backdrop.right,
backdrop.bottom));
backdropNode.draw(canvas);
}
if (content.bottom < backdrop.bottom) {
// 将背景绘制到内容的底部
//注意:底部填充使用左/右的内容,以避免覆盖左/右填充
SkAutoCanvasRestore acr(canvas, true);
canvas->clipRect(SkRect::MakeLTRB(content.left, content.bottom, content.right,
backdrop.bottom));
backdropNode.draw(canvas);
}
}
// 绘制内容nodes及背景的左/上
RenderNodeDrawable contentNode(nodes[1].get(), canvas);
if (!backdrop.isEmpty()) {
// 计算相对背景x、y的偏移,然后做变换
float dx = backdrop.left - contentDrawBounds.left;
float dy = backdrop.top - contentDrawBounds.top;
SkAutoCanvasRestore acr(canvas, true);
canvas->translate(dx, dy);
const SkRect contentLocalClip =
SkRect::MakeXYWH(contentDrawBounds.left, contentDrawBounds.top,
backdrop.getWidth(), backdrop.getHeight());
canvas->clipRect(contentLocalClip);
contentNode.draw(canvas);
} else {
SkAutoCanvasRestore acr(canvas, true);
contentNode.draw(canvas);
}
//最后绘制剩余的-附加的覆盖节点
for (size_t index = 2; index < nodes.size(); index++) {
if (!nodes[index]->nothingToDraw()) {
SkAutoCanvasRestore acr(canvas, true);
RenderNodeDrawable overlayNode(nodes[index].get(), canvas);
overlayNode.draw(canvas);
}
}
...
}
SkiaPipeline::renderFrameImpl执行Draw流程, 先绘制背景(内容+标题)的右侧和底部区域,然后绘制内容+背景的左上区域,主要是调用RenderNodeDrawable:draw去绘制,RenderNodeDrawable继承SkDrawable,通过SkDrawable::draw调用RenderNodeDrawable::forceDraw->RenderNodeDrawable::drawContent->SkiaDisplayList::draw->DisplayListData::draw->SkCanvas::onDrawImageRect2
进入到设备的GPU绘制中:skgpu::v1::Device::drawImageRect,这里主要执行两个流程
- 调用GrGLGpu::createTexture去创建texture,创建纹理
- 创建纹理成功后,调用SurfaceDrawContext::addDrawOp,进而调用OpsTask::addDrawOp
external/skia/src/gpu/v1/Device.cpp
void Device::drawImageRect(const SkImage* image,
const SkRect* src,
const SkRect& dst,
const SkSamplingOptions& sampling,
const SkPaint& paint,
SkCanvas::SrcRectConstraint constraint) {
...
this->drawImageQuad(image, src, &dst, nullptr, aa, aaFlags, nullptr, sampling, paint,
constraint);
}
external/skia/src/gpu/v1/Device_drawTexture.cpp
void Device::drawImageQuad(const SkImage* image,
const SkRect* srcRect,
const SkRect* dstRect,
const SkPoint dstClip[4],
GrAA aa,
GrQuadAAFlags aaFlags,
const SkMatrix* preViewMatrix,
const SkSamplingOptions& origSampling,
const SkPaint& paint,
SkCanvas::SrcRectConstraint constraint) {
...
draw_image(fContext.get(),
fSurfaceDrawContext.get(),
clip,
matrixProvider,
paint,
*as_IB(image),
src,
dst,
dstClip,
srcToDst,
aa,
aaFlags,
constraint,
sampling);
return;
}
void draw_image(GrRecordingContext* rContext,
skgpu::v1::SurfaceDrawContext* sdc,
const GrClip* clip,
const SkMatrixProvider& matrixProvider,
const SkPaint& paint,
const SkImage_Base& image,
const SkRect& src,
const SkRect& dst,
const SkPoint dstClip[4],
const SkMatrix& srcToDst,
GrAA aa,
GrQuadAAFlags aaFlags,
SkCanvas::SrcRectConstraint constraint,
SkSamplingOptions sampling,
SkTileMode tm = SkTileMode::kClamp) {
...
if (tm == SkTileMode::kClamp &&
!image.isYUVA() &&
can_use_draw_texture(paint, sampling.useCubic, sampling.mipmap)) {
// We've done enough checks above to allow us to pass ClampNearest() and not check for
// scaling adjustments.
1. 调用asView创建纹理
auto [view, ct] = image.asView(rContext, GrMipmapped::kNo);
if (!view) {
return;
}
GrColorInfo info(image.imageInfo().colorInfo());
info = info.makeColorType(ct);
2. add Draw
draw_texture(sdc,
clip,
ctm,
paint,
sampling.filter,
src,
dst,
dstClip,
aa,
aaFlags,
constraint,
std::move(view),
info);
return;
}
// 下面是类似底部导航栏这种的图片绘制
1.. createTexture
std::unique_ptr<GrFragmentProcessor> fp = image.asFragmentProcessor(rContext,
sampling,
tileModes,
textureMatrix,
subset,
domain);
2.. 底部导航栏这种通过path绘制的图标,一般会走这里
if (!mf) {
// Can draw the image directly (any mask filter on the paint was converted to an FP already)
if (dstClip) {
SkPoint srcClipPoints[4];
SkPoint* srcClip = nullptr;
if (canUseTextureCoordsAsLocalCoords) {
// Calculate texture coordinates that match the dst clip
GrMapRectPoints(dst, src, dstClip, srcClipPoints, 4);
srcClip = srcClipPoints;
}
sdc->fillQuadWithEdgeAA(clip, std::move(grPaint), aa, aaFlags, ctm, dstClip, srcClip);
} else {
// Provide explicit texture coords when possible, otherwise rely on texture matrix
sdc->fillRectWithEdgeAA(clip, std::move(grPaint), aa, aaFlags, ctm, dst,
canUseTextureCoordsAsLocalCoords ? &src : nullptr);
}
}
}
void draw_texture(skgpu::v1::SurfaceDrawContext* sdc,
const GrClip* clip,
const SkMatrix& ctm,
const SkPaint& paint,
GrSamplerState::Filter filter,
const SkRect& srcRect,
const SkRect& dstRect,
const SkPoint dstClip[4],
GrAA aa,
GrQuadAAFlags aaFlags,
SkCanvas::SrcRectConstraint constraint,
GrSurfaceProxyView view,
const GrColorInfo& srcColorInfo) {
SkPMColor4f color = texture_color(paint.getColor4f(), 1.f, srcColorInfo.colorType(), dstInfo);
if (dstClip) {
// Get source coords corresponding to dstClip
SkPoint srcQuad[4];
GrMapRectPoints(dstRect, srcRect, dstClip, srcQuad, 4);
sdc->drawTextureQuad(clip,
std::move(view),
srcColorInfo.colorType(),
srcColorInfo.alphaType(),
filter,
GrSamplerState::MipmapMode::kNone,
paint.getBlendMode_or(SkBlendMode::kSrcOver),
color,
srcQuad,
dstClip,
aa,
aaFlags,
constraint == SkCanvas::kStrict_SrcRectConstraint ? &srcRect : nullptr,
ctm,
std::move(textureXform));
} else {
sdc->drawTexture(clip,
std::move(view),
srcColorInfo.alphaType(),
filter,
GrSamplerState::MipmapMode::kNone,
paint.getBlendMode_or(SkBlendMode::kSrcOver),
color,
srcRect,
dstRect,
aa,
aaFlags,
constraint,
ctm,
std::move(textureXform));
}
}
Device_drawTexture.cpp的draw_image中,主要两个操作创建纹理或者addDraw
分两种情况:
- ImageVIew这种资源图片的
- 调用SkImage_Base::asView去创建纹理
SkImage_Gpu和SkImage_Raster继承SkImage_Base,实现了onAsView,在asView->onAsView中去创建纹理,这里会调用SkImage_Raster::onAsView- 调用调用draw_texture去add draw
draw_texture中主要是调用SurfaceDrawContext::drawTexture/drawTextureQuad,进而去调用SurfaceDrawContext::addDrawOp->OpsTask::addDrawOp添加到fOpChains中
那么这里的图片资源怎么加载的呢,怎么知道要使用SkImage_Raster还是SkImage_Gpu呢?
可以看到下面的trace中,总共有两次
针对mimap里面的图片,会执行两次创建,why?
主线程在startActivity执行inflate时,回去加载资源图片,因为调用了ImageView.setImageResource,进而调用ImageView.resolveUri去执行getDrawable去加载资源,调用android.content.Context.getDrawable时,调用到ImageDecoder.decodeDrawable->ImageDecoder_nDecodeBitmap->ImageDecoder::decode,两次前面流程一样,下面分别调用
a. SkImage::MakeFromBitmap->SkImage_Raster.SkMakeImageFromRasterBitmap
b. SkCanvas::drawImage->SkCanvas::onDrawImage2->SkBitmapDevice::drawImageRect->SkDraw::drawBitmap->make_paint_with_image->SkMakeBitmapShaderForPaint->SkImage_Raster.SkMakeImageFromRasterBitmap在第一次draw的时候,调用android.view.ViewGroup.drawChild->BaseRecordingCanvas.drawBitmap->CanvasJNI::drawBitmapRect->SkiaRecordingCanvas::drawBitmap进而调用SkImage_Raster::SkMakeImageFromRasterBitmapPriv去创建SkImage_Raster对象,进而去创建纹理
那么这两次有什么区别呢
external/skia/src/image/SkImage_Raster.cpp
sk_sp<SkImage> SkMakeImageFromRasterBitmapPriv(const SkBitmap& bm, SkCopyPixelsMode cpm,
uint32_t idForCopy) {
if (kAlways_SkCopyPixelsMode == cpm || (!bm.isImmutable() && kNever_SkCopyPixelsMode != cpm)) {
SkPixmap pmap;
if (bm.peekPixels(&pmap)) {
return MakeRasterCopyPriv(pmap, idForCopy);
} else {
return sk_sp<SkImage>();
}
}
ATRACE_ANDROID_FRAMEWORK_ALWAYS("SkImage_Raster::SkMakeImageFromRasterBitmapPriv");
return sk_make_sp<SkImage_Raster>(bm, kNever_SkCopyPixelsMode == cpm);
}
- 通过path绘制图片的
- .调用SkImage_Base::asFragmentProcessor进而调用到GrGLGpu::createTexture创建纹理
- 调用fillQuadWithEdgeAA去执行draw流程
- 真正的纹理创建流程
SkImage_Raster::onAsView调用到GrMakeCachedBitmapProxyView先创建Bitmap,这里调用GrProxyProvider::createProxyFromBitmap->createNonMippedProxyFromBitmap,这里主要调用GrSurfaceProxyPriv::doLazyInstantiation,调用GrProxyProvider::createNonMippedProxyFromBitmap后,调用GrResourceProvider::createTexture,这里主要执行两个流程
- 调用GrGpu::createTextureCommon->GrGLGpu::onCreateTexture->GrGLGpu::createTexture创建纹理
- 调用GrGpu::createTexture->GrGpu::writePixels执行 Texture upload流程
external/skia/src/image/SkImage_Raster.cpp
#if SK_SUPPORT_GPU
std::tuple<GrSurfaceProxyView, GrColorType> SkImage_Raster::onAsView(
GrRecordingContext* rContext,
GrMipmapped mipmapped,
GrImageTexGenPolicy policy) const {
ATRACE_ANDROID_FRAMEWORK_ALWAYS("SkImage_Raster::onAsView");
...
if (policy == GrImageTexGenPolicy::kDraw) {
return GrMakeCachedBitmapProxyView(rContext, fBitmap, mipmapped);
}
...
}
* external/skia/src/gpu/GrProxyProvider.cpp
sk_sp<GrTextureProxy> GrProxyProvider::createProxyFromBitmap(const SkBitmap& bitmap,
GrMipmapped mipMapped,
SkBackingFit fit,
SkBudgeted budgeted) {
...
ATRACE_ANDROID_FRAMEWORK_ALWAYS("GrProxyProvider::createProxyFromBitmap:Upload %sTexture [%ux%u]",
GrMipmapped::kYes == mipMapped ? "MipMap " : "",
bitmap.width(), bitmap.height());
...
sk_sp<GrTextureProxy> proxy;
if (mipMapped == GrMipmapped::kNo ||
0 == SkMipmap::ComputeLevelCount(copyBitmap.width(), copyBitmap.height())) {
// 这里调用createNonMippedProxyFromBitmap,即创建GrTextureProxy
proxy = this->createNonMippedProxyFromBitmap(copyBitmap, fit, budgeted);
} else {
proxy = this->createMippedProxyFromBitmap(copyBitmap, budgeted);
}
if (!proxy) {
return nullptr;
}
auto direct = fImageContext->asDirectContext();
if (direct) {
GrResourceProvider* resourceProvider = direct->priv().resourceProvider();
// priv直接返回GrTextureProxy,去创建对应对象的实例,执行纹理创建
if (!proxy->priv().doLazyInstantiation(resourceProvider)) {
return nullptr;
}
}
return proxy;
}
sk_sp<GrTextureProxy> GrProxyProvider::createNonMippedProxyFromBitmap(const SkBitmap& bitmap,
SkBackingFit fit,
SkBudgeted budgeted) {
auto dims = bitmap.dimensions();
ATRACE_ANDROID_FRAMEWORK_ALWAYS("GrProxyProvider::createNonMippedProxyFromBitmap");
...
// 这里在创建时候,回调GrTextureProxy::createTexture
sk_sp<GrTextureProxy> proxy = this->createLazyProxy(
[bitmap](GrResourceProvider* resourceProvider, const LazySurfaceDesc& desc) {
SkASSERT(desc.fMipmapped == GrMipmapped::kNo);
GrMipLevel mipLevel = {bitmap.getPixels(), bitmap.rowBytes(), nullptr};
auto colorType = SkColorTypeToGrColorType(bitmap.colorType());
return LazyCallbackResult(resourceProvider->createTexture(
desc.fDimensions,
desc.fFormat,
desc.fTextureType,
colorType,
desc.fRenderable,
desc.fSampleCnt,
desc.fBudgeted,
desc.fFit,
desc.fProtected,
mipLevel));
},
format, dims, GrMipmapped::kNo, GrMipmapStatus::kNotAllocated,
GrInternalSurfaceFlags::kNone, fit, budgeted, GrProtected::kNo, UseAllocator::kYes);
...
return proxy;
}
* external/skia/src/gpu/GrSurfaceProxyPriv.h
inline GrSurfaceProxyPriv GrSurfaceProxy::priv() { return GrSurfaceProxyPriv(this); }
* external/skia/src/gpu/GrGpu.cpp
sk_sp<GrTexture> GrGpu::createTexture(SkISize dimensions,
const GrBackendFormat& format,
GrTextureType textureType,
GrRenderable renderable,
int renderTargetSampleCnt,
SkBudgeted budgeted,
GrProtected isProtected,
GrColorType textureColorType,
GrColorType srcColorType,
const GrMipLevel texels[],
int texelLevelCount) {
ATRACE_ANDROID_FRAMEWORK_ALWAYS("GrGpu::createTexture");
...
auto tex = this->createTextureCommon(dimensions,
format,
textureType,
renderable,
renderTargetSampleCnt,
budgeted,
isProtected,
texelLevelCount,
levelClearMask);
...
if (!this->writePixels(tex.get(),
SkIRect::MakeSize(dimensions),
textureColorType,
srcColorType,
texels,
texelLevelCount)) {
return nullptr;
}
...
return tex;
}
bool GrGpu::writePixels(GrSurface* surface,
SkIRect rect,
GrColorType surfaceColorType,
GrColorType srcColorType,
const GrMipLevel texels[],
int mipLevelCount,
bool prepForTexSampling) {
ATRACE_ANDROID_FRAMEWORK_ALWAYS("GrGpu::writePixels Texture upload(%u) %ix%i",
surface->uniqueID().asUInt(), rect.width(), rect.height());
...
if (this->onWritePixels(surface,
rect,
surfaceColorType,
srcColorType,
texels,
mipLevelCount,
prepForTexSampling)) {
this->didWriteToSurface(surface, kTopLeft_GrSurfaceOrigin, &rect, mipLevelCount);
fStats.incTextureUploads();
return true;
}
return false;
}
* external/skia/src/gpu/gl/GrGLGpu.cpp
GrGLuint GrGLGpu::createTexture(SkISize dimensions,
GrGLFormat format,
GrGLenum target,
GrRenderable renderable,
GrGLTextureParameters::SamplerOverriddenState* initialState,
int mipLevelCount,
GrProtected isProtected) {
...
GrGLuint id = 0;
// 产生一个纹理Id
GL_CALL(GenTextures(1, &id));
...
// 调用GLBindTexture,使用这个纹理id,或者叫绑定(关联)
this->bindTextureToScratchUnit(target, id);
...
bool success = false;
if (internalFormat) {
if (this->glCaps().formatSupportsTexStorage(format)) {
...
} else {
GrGLenum externalFormat, externalType;
this->glCaps().getTexImageExternalFormatAndType(format, &externalFormat, &externalType);
GrGLenum error = GR_GL_NO_ERROR;
if (externalFormat && externalType) {
for (int level = 0; level < mipLevelCount && error == GR_GL_NO_ERROR; level++) {
const int twoToTheMipLevel = 1 << level;
const int currentWidth = std::max(1, dimensions.width() / twoToTheMipLevel);
const int currentHeight = std::max(1, dimensions.height() / twoToTheMipLevel);
// 指定二维纹理图像
error = GL_ALLOC_CALL(TexImage2D(target, level, internalFormat, currentWidth,
currentHeight, 0, externalFormat, externalType,
nullptr));
}
success = (error == GR_GL_NO_ERROR);
}
}
}
if (success) {
return id;
}
GL_CALL(DeleteTextures(1, &id));
return 0;
}
bool GrGLGpu::onWritePixels(GrSurface* surface,
SkIRect rect,
GrColorType surfaceColorType,
GrColorType srcColorType,
const GrMipLevel texels[],
int mipLevelCount,
bool prepForTexSampling) {
...
ATRACE_ANDROID_FRAMEWORK_ALWAYS("GrGLGpu::onWritePixels (%u) %ix%i",
surface->uniqueID().asUInt(), rect.width(), rect.height());
// If we have mips make sure the base/max levels cover the full range so that the uploads go to
// the right levels. We've found some Radeons require this.
if (mipLevelCount && this->glCaps().mipmapLevelControlSupport()) {
auto params = glTex->parameters();
GrGLTextureParameters::NonsamplerState nonsamplerState = params->nonsamplerState();
int maxLevel = glTex->maxMipmapLevel();
if (params->nonsamplerState().fBaseMipMapLevel != 0) {
ATRACE_ANDROID_FRAMEWORK_ALWAYS("GrGLGpu::onWritePixels TexParameteri base ");
GL_CALL(TexParameteri(glTex->target(), GR_GL_TEXTURE_BASE_LEVEL, 0));
nonsamplerState.fBaseMipMapLevel = 0;
}
if (params->nonsamplerState().fMaxMipmapLevel != maxLevel) {
ATRACE_ANDROID_FRAMEWORK_ALWAYS("GrGLGpu::onWritePixels TexParameteri max ");
GL_CALL(TexParameteri(glTex->target(), GR_GL_TEXTURE_MAX_LEVEL, maxLevel));
nonsamplerState.fBaseMipMapLevel = maxLevel;
}
params->set(nullptr, nonsamplerState, fResetTimestampForTextureParameters);
}
...
return this->uploadColorTypeTexData(glTex->format(),
surfaceColorType,
glTex->dimensions(),
glTex->target(),
rect,
srcColorType,
texels,
mipLevelCount);
}
void GrGLGpu::uploadTexData(SkISize texDims,
GrGLenum target,
SkIRect dstRect,
GrGLenum externalFormat,
GrGLenum externalType,
size_t bpp,
const GrMipLevel texels[],
int mipLevelCount) {
...
const GrGLCaps& caps = this->glCaps();
bool restoreGLRowLength = false;
ATRACE_ANDROID_FRAMEWORK_ALWAYS("GrGLGpu::uploadTexData");
this->unbindXferBuffer(GrGpuBufferType::kXferCpuToGpu);
// 指定内存中每个像素行开始的对齐要求。 允许的值为 1 (字节对齐) 、2 (行与偶数字节对齐) 、4 (字对齐) ,8 (行从双字边界开始) 。
GL_CALL(PixelStorei(GR_GL_UNPACK_ALIGNMENT, 1));
SkISize dims = dstRect.size();
for (int level = 0; level < mipLevelCount; ++level, dims = {std::max(dims.width() >> 1, 1),
std::max(dims.height() >> 1, 1)}) {
if (!texels[level].fPixels) {
continue;
}
const size_t trimRowBytes = dims.width() * bpp;
const size_t rowBytes = texels[level].fRowBytes;
if (caps.writePixelsRowBytesSupport() && (rowBytes != trimRowBytes || restoreGLRowLength)) {
GrGLint rowLength = static_cast<GrGLint>(rowBytes / bpp);
GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, rowLength));
restoreGLRowLength = true;
} else {
SkASSERT(rowBytes == trimRowBytes);
}
GL_CALL(TexSubImage2D(target, level, dstRect.x(), dstRect.y(), dims.width(), dims.height(),
externalFormat, externalType, texels[level].fPixels));
}
if (restoreGLRowLength) {
SkASSERT(caps.writePixelsRowBytesSupport());
GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0));
}
}
创建纹理
- 调用GenTextures,产生一个纹理Id,可以认为是纹理句柄,后面的操作将书用这个纹理id
- 调用glBindTexture,使用这个纹理id,或者叫绑定(关联)
- 调用glTexImage2D,指定二维纹理图像
设置纹理参数
onWritePixels中,首先调用 GL_CALL(TexParameteri(glTex->target(), GR_GL_TEXTURE_MAX_LEVEL, maxLevel))设置mipmaps的层级,然后调用GrGLGpu::uploadColorTypeTexData->GrGLGpu::uploadTexData
创建纹理demo
virtual unsigned loadTexture(const char* fileName)
{
unsigned textureId = 0;
//1 获取图片格式
FREE_IMAGE_FORMAT fifmt = FreeImage_GetFileType(fileName, 0);
//2 加载图片
FIBITMAP *dib = FreeImage_Load(fifmt, fileName,0);
//3 转化为rgb 24色
dib = FreeImage_ConvertTo24Bits(dib);
//4 获取数据指针
BYTE *pixels = (BYTE*)FreeImage_GetBits(dib);
int width = FreeImage_GetWidth(dib);
int height = FreeImage_GetHeight(dib);
//windows是BGR模式
for (int i =0;i<width*height*3;)
{
float temp = pixels[i+2];
pixels[i + 2] = pixels[i];
pixels[i] = temp;
i += 3;
}
/**
* 产生一个纹理Id,可以认为是纹理句柄,后面的操作将书用这个纹理id
*/
glGenTextures( 1, &textureId );
/**
* 使用这个纹理id,或者叫绑定(关联)
*/
glBindTexture( GL_TEXTURE_2D, textureId );
/**
* 指定纹理的放大,缩小滤波,使用线性方式,即当图片放大的时候插值方式
*/
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
/**
* 将图片的rgb数据上传给opengl.
*/
glTexImage2D(
GL_TEXTURE_2D, //! 指定是二维图片
0, //! 指定为第一级别,纹理可以做mipmap,即lod,离近的就采用级别大的,远则使用较小的纹理
GL_RGB, //! 纹理的使用的存储格式
width, //! 宽度,老一点的显卡,不支持不规则的纹理,即宽度和高度不是2^n。
height, //! 宽度,老一点的显卡,不支持不规则的纹理,即宽度和高度不是2^n。
0, //! 是否的边
GL_RGB, //! 数据的格式,bmp中,windows,操作系统中存储的数据是bgr格式
GL_UNSIGNED_BYTE, //! 数据是8bit数据
pixels
);
char subData[100 * 100 * 3];
memset(subData, 255, sizeof(subData));
for (int i = 0; i<150;)
{
subData[i] = 0;
subData[++i] = 0;
subData[++i] = 255;
}
glTexSubImage2D(GL_TEXTURE_2D, 0, 100, 50, 100, 100, GL_RGB, GL_UNSIGNED_BYTE, subData);
/**
* 释放内存
*/
FreeImage_Unload(dib);
return textureId;
}
OPENGL 设置纹理参数glTextureParameter
OpenGL中设置纹理参数的API接口为glTextureParameter,我们所有的纹理参数都由这个接口设置,下面我们介绍几种常用的纹理参数的配置。
- 采样:Wrapping
纹理坐标的范围与OpenGL的屏幕坐标范围一样,是0-1。超出这一范围的坐标将被OpenGL根据GL_TEXTURE_WRAP参数的值进行处理:
GL_REPEAT: 超出纹理范围的坐标整数部分被忽略,形成重复效果。
GL_MIRRORED_REPEAT: 超出纹理范围的坐标整数部分被忽略,但当整数部分为奇数时进行取反,形成镜像效果。
GL_CLAMP_TO_EDGE:超出纹理范围的坐标被截取成0和1,形成纹理边缘延伸的效果。
GL_CLAMP_TO_BORDER: 超出纹理范围的部分被设置为边缘色。
- 过滤
由于纹理坐标和我们当前的屏幕分辨率是无关的,所以当我们为一个模型贴纹理时,往往会遇到纹理尺寸与模型尺寸不符的情况,这时,纹理会因为缩放而失真。处理这一失真的过程我们称为过滤,在OpenGL中我们有如下几种常用的过滤手段:
GL_NEAREST: 最临近过滤,获得最靠近纹理坐标点的像素。
GL_LINEAR: 线性插值过滤,获取坐标点附近4个像素的加权平均值。
GL_NEAREST_MIPMAP_NEAREST:用于mipmap,下节将详细介绍。
GL_LINEAR_MIPMAP_NEAREST:
GL_NEAREST_MIPMAP_LINEAR:
GL_LINEAR_MIPMAP_LINEAR:
我们可以单独为纹理缩放指定不同的过滤算法,这两种情况下纹理参数设置分别对应为:GL_TEXTURE_MIN_FILTER和GL_TEXTURE_MAG_FILTER.
- 纹理映射 Mipmaps
Mipmaps是一个功能强大的纹理技术,它可以提高渲染的性能以及提升场景的视觉质量。它可以用来解决使用一般的纹理贴图会出现的两个常见的问题:
1.闪烁,当屏幕上被渲染区域与它所应用的纹理图像相比显得非常小时,就会出现闪烁。尤其当视口和物体在移动的时候,这种负面效果更容易被看到。
2.性能问题。如果我们的贴纹理的区域离我们非常远,远到在屏幕中只有一个像素那么大小时,纹理的所有纹素都集中在这一像素中。这时,我们无论做邻近过滤还是做线性过滤时都不得不将纹理的所有纹素计算在内,这种计算效率将大大影响我们的采样效率,而纹理的数据量越大,对效率的影响就会更大。
使用Mipmaps技术就可以解决上面那两个问题。当加载纹理的同时预处理生成一系列从大到小的纹理,使用时只需选择合适大小的纹理加载就行了。这样虽然会增加一些额外的内存(一个正方形纹理将额外占用约30%的内存),但将大大提高了我们的采样效率和采样质量。
生成mipmaps的过程很简单,只需要在加载纹理后执行下面一行代码:
使用mipmaps也很简单,只需设置过滤参数为以下4种中的任意一种:
GL_NEAREST_MIPMAP_NEAREST:选择最邻近的mip层,并使用最邻近过滤。
GL_NEAREST_MIPMAP_LINEAR:对两个mip层使用最邻近过滤后的采样结果进行加权平均。
GL_LINEAR_MIPMAP_NEAREST:选择最邻近的mip层,使用线性插值算法进行过滤。
GL_LINEAR_MIPMAP_LINEAR:对两个mip层使用线性插值过滤后的采样结果进行加权平均,又称三线性mipmap。
在选择这几种过滤方法时,我们需要考虑的是效率和质量,线性过滤往往更加平滑,但随之而来的是更多的采样次数;而临近过滤减少了采样次数,但最终视觉效果会比较差。- mipmaps的层级
mipmap有多少个层级是有glTexImage1D、glTexImage2D载入纹理的第二个参数level决定的。 层级从0开始,0,1,2,3这样递增,如果没有使用mipmap技术,只有第0层的纹理会被加载,OpenGL会根据给定的几何图像的大小选择最合适的纹理。
在默认情况下, 为了使用mipmap,所有层级都会被加载,但是我们可以用纹理参数来控制要加载的层级范围:
使用glTexParameteri, 设定纹理参数。
参数为GL_TEXTURE_BASE_LEVEL来指定最低层级的level
参数为GL_TEXTURE_MAX_LEVEL来指定最高层级的level
OPENGL 对齐像素字节glPixelStorei
从本地内存向GPU的传输(UNPACK),包括各种glTexImage、glDrawPixel;从GPU到本地内存的传输(PACK),包括glGetTexImage、glReadPixel等
官方介绍,参考文档:glPixelStorei 函数
- GL_UNPACK_ALIGNMENT
glPixelStorei(GL_UNPACK_ALIGNMENT,1)控制的是所读取数据的按照字节对齐方式对齐,默认4字节对齐,即一行的图像数据字节数必须是4的整数倍,即读取数据时,读取4个字节用来渲染一行,之后读取4字节数据用来渲染第二行。对RGB 3字节像素而言,若一行10个像素,即30个字节,在4字节对齐模式下,OpenGL会读取32个字节的数据,若不加注意,会导致glTextImage中致函数的读取越界,从而全面崩溃。
- GL_UNPACK_ROW_LENGTH/GL_UNPACK_SKIP_ROWS /GL_UNPACK_SKIP_PIXELS
有的时候,我们把一些小图片拼凑进一张大图片内,这样使用大图片生成的纹理,一来可以使多个原本使用不同的图片作为纹理的同质物件如今能够在同一个Batch内,节省了一些状态切换的开销,二来也容易综合地降低了显存中纹理的总大小。但是,也有些时候,我们需要从原本一张大的图片中,截取图片当中的某一部分作为纹理。要能够做到这样,可以通过预先对图片进行裁剪或者在获得像素数据后,把其中需要的那一部分另外存储到一个Buffer内再交给glTexImage2D之类的函数。而上述这些参数下glPixelStore的使用将帮助我们更好地完成这个目的:
//原图中需要单独提取出来制成纹理的区域
RECT subRect = {{100, 80}, {500, 400}}; //origin.x, origin.y, size.width, size.height
//假设原图的宽度为BaseWidth, 高度为BaseHeight
glPixelStorei(GL_UNPACK_ROW_LENGTH, BaseWidth); //指定像素数据中原图的宽度
glPixelStorei(GL_UNPACK_SKIP_ROWS, subRect. origin.y.); //指定纹理起点偏离原点的高度值
glPixelStorei(GL_UNPACK_SKIP_PIXELS, subRect. origin.x); //指定纹理起点偏离原点的宽度值
OPENGL 指定二维纹理图像 glTexImage2D
…
OPENGL 指定现有一维纹理图像的一部分 glTexSubImage2D
glTexSubImage2D 函数指定现有一维纹理图像的一部分。 不能使用 glTexSubImage2D 定义新纹理。
纹理bind流程
external/skia/src/gpu/ops/OpsTask.cpp
void OpsTask::addDrawOp(GrDrawingManager* drawingMgr, GrOp::Owner op, bool usesMSAA,
const GrProcessorSet::Analysis& processorAnalysis, GrAppliedClip&& clip,
const GrDstProxyView& dstProxyView,
GrTextureResolveManager textureResolveManager, const GrCaps& caps) {
...
this->recordOp(std::move(op), usesMSAA, processorAnalysis, clip.doesClip() ? &clip : nullptr,
&dstProxyView, caps);
}
void OpsTask::recordOp(
GrOp::Owner op, bool usesMSAA, GrProcessorSet::Analysis processorAnalysis,
GrAppliedClip* clip, const GrDstProxyView* dstProxyView, const GrCaps& caps) {
...
// Check if there is an op we can combine with by linearly searching back until we either
// 1) check every op
// 2) intersect with something
// 3) find a 'blocker'
...
if (clip) {
clip = fArenas->arenaAlloc()->make<GrAppliedClip>(std::move(*clip));
SkDEBUGCODE(fNumClips++;)
}
fOpChains.emplace_back(std::move(op), processorAnalysis, clip, dstProxyView);
}
在recordOp中将被添加到fOpChains中
external/skia/src/gpu/ops/OpsTask.h
// For ops/opsTask we have mean: 5 stdDev: 28
SkSTArray<25, OpChain> fOpChains;
- 纹理创建完成后,执行surface->flushAndSubmit去完成真正的绘制流程
这里会执行bindTexture去绑定纹理,调用堆栈如下
#00 pc 00000000005b7eec /system/lib64/libhwui.so (GrGLGpu::bindTexture(int, GrSamplerState, skgpu::Swizzle const&, GrGLTexture*)+96)
#01 pc 00000000005c4290 /system/lib64/libhwui.so (GrGLProgram::bindTextures(GrGeometryProcessor const&, GrSurfaceProxy const* const*, GrPipeline const&)+172)
#02 pc 00000000005c2704 /system/lib64/libhwui.so (GrGLOpsRenderPass::onBindTextures(GrGeometryProcessor const&, GrSurfaceProxy const* const*, GrPipeline const&)+84)
#03 pc 0000000000548934 /system/lib64/libhwui.so (GrOpsRenderPass::bindTextures(GrGeometryProcessor const&, GrSurfaceProxy const* const*, GrPipeline const&)+36)
#04 pc 000000000060f084 /system/lib64/libhwui.so ((anonymous namespace)::ShadowCircularRRectOp::onExecute(GrOpFlushState*, SkRect const&)+172)
#05 pc 0000000000605988 /system/lib64/libhwui.so (skgpu::v1::OpsTask::onExecute(GrOpFlushState*)+772)
#06 pc 00000000005367b8 /system/lib64/libhwui.so (GrDrawingManager::flush(SkSpan<GrSurfaceProxy*>, SkSurface::BackendSurfaceAccess, GrFlushInfo const&, GrBackendSurfaceMutableState const*)+2780)
#07 pc 0000000000536d44 /system/lib64/libhwui.so (GrDrawingManager::flushSurfaces(SkSpan<GrSurfaceProxy*>, SkSurface::BackendSurfaceAccess, GrFlushInfo const&, GrBackendSurfaceMutableState const*)+164)
#08 pc 000000000052f5f0 /system/lib64/libhwui.so (GrDirectContextPriv::flushSurfaces(SkSpan<GrSurfaceProxy*>, SkSurface::BackendSurfaceAccess, GrFlushInfo const&, GrBackendSurfaceMutableState const*)+284)
#09 pc 000000000068c6bc /system/lib64/libhwui.so (SkSurface_Gpu::onFlush(SkSurface::BackendSurfaceAccess, GrFlushInfo const&, GrBackendSurfaceMutableState const*)+152)
#10 pc 000000000068e688 /system/lib64/libhwui.so (SkSurface::flushAndSubmit(bool)+64)
#11 pc 000000000027c2bc /system/lib64/libhwui.so (android::uirenderer::skiapipeline::SkiaOpenGLPipeline::draw(android::uirenderer::renderthread::Frame const&, SkRect const&, SkRect const&, android::uirenderer::LightGeometry const&, android::uirenderer::LayerUpdateQueue*, android::uirenderer::Rect const&, bool, android::uirenderer::LightInfo const&, std::__1::vector<android::sp<android::uirenderer::RenderNode>, std::__1::allocator<android::sp<android::uirenderer::RenderNode> > > const&, android::uirenderer::FrameInfoVisualizer*)+604)
#12 pc 0000000000283974 /system/lib64/libhwui.so (android::uirenderer::renderthread::CanvasContext::draw()+1104)
#13 pc 00000000002866b4 /system/lib64/libhwui.so (std::__1::__function::__func<android::uirenderer::renderthread::DrawFrameTask::postAndWait()::$_0, std::__1::allocator<android::uirenderer::renderthread::DrawFrameTask::postAndWait()::$_0>, void ()>::operator()() (.c1671e787f244890c877724752face20)+904)
#14 pc 0000000000276090 /system/lib64/libhwui.so (android::uirenderer::WorkQueue::process()+588)
#15 pc 00000000002972c0 /system/lib64/libhwui.so (android::uirenderer::renderthread::RenderThread::threadLoop()+416)
#16 pc 0000000000013598 /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+424)
#17 pc 00000000000f5548 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+208)
#18 pc 000000000008ef3c /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+68)
送显流程
SkiaOpenGLPipeline::swapBuffers
skia中打印trace方式
ATRACE_ANDROID_FRAMEWORK_ALWAYS("SurfaceDrawContext::drawTexturedQuad");
打印堆栈方式
#include <utils/CallStack.h>
static void debug_stack() {
android::CallStack stack("zzh");
}