SurfaceFlinger学习笔记(七)之SKIA
创始人
2024-05-20 19:59:32
0

关于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

  1. 调用EglManager.damageFrame主要是部分更新参数的设置,前面我们也damage的区域就是前面Prepare时累加器累加出来的
  2. 调用renderFrame进行纹理创建,实现在基类SkiaPipeline::renderFrame中
  3. 调用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>& renderNodes,FrameInfoVisualizer* profiler) {1. damageFramemEglManager.damageFrame(frame, dirty);...// 这里调用SkSurface::MakeFromBackendRenderTargetsk_sp surface(SkSurface::MakeFromBackendRenderTarget(mRenderThread.getGrContext(), backendRT, this->getSurfaceOrigin(), colorType,mSurfaceColorSpace, &props));LightingInfo::updateLighting(lightGeometry, lightInfo);2. damageFramerenderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,SkMatrix::I());...
{ATRACE_NAME("flush commands");3. damageFramesurface->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, 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, std::__1::allocator > > 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, std::__1::allocator > > const&, bool, android::uirenderer::Rect const&, sk_sp, 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, std::__1::allocator > > 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, 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.cppvoid SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,const std::vector>& nodes, bool opaque,const Rect& contentDrawBounds, sk_sp 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 frontrenderLayersImpl(layers, opaque);renderFrameImpl(clip, nodes, opaque, contentDrawBounds, canvas, preTransform);endCapture(surface.get());
...Properties::skpCaptureEnabled = previousSkpEnabled;
}
void SkiaPipeline::renderFrameImpl(const SkRect& clip,const std::vector>& 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,这里主要执行两个流程

  1. 调用GrGLGpu::createTexture去创建texture,创建纹理
  2. 创建纹理成功后,调用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 Drawdraw_texture(sdc,clip,ctm,paint,sampling.filter,src,dst,dstClip,aa,aaFlags,constraint,std::move(view),info);return;}
// 下面是类似底部导航栏这种的图片绘制
1.. createTexture
std::unique_ptr 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 clipGrMapRectPoints(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 matrixsdc->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 dstClipSkPoint 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
分两种情况:

  1. 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中,总共有两次

  1. 针对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

  2. 在第一次draw的时候,调用android.view.ViewGroup.drawChild->BaseRecordingCanvas.drawBitmap->CanvasJNI::drawBitmapRect->SkiaRecordingCanvas::drawBitmap进而调用SkImage_Raster::SkMakeImageFromRasterBitmapPriv去创建SkImage_Raster对象,进而去创建纹理

那么这两次有什么区别呢

在这里插入图片描述

在这里插入图片描述

external/skia/src/image/SkImage_Raster.cppsk_sp 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();}}ATRACE_ANDROID_FRAMEWORK_ALWAYS("SkImage_Raster::SkMakeImageFromRasterBitmapPriv");return sk_make_sp(bm, kNever_SkCopyPixelsMode == cpm);
}
  1. 通过path绘制图片的
  • .调用SkImage_Base::asFragmentProcessor进而调用到GrGLGpu::createTexture创建纹理
  • 调用fillQuadWithEdgeAA去执行draw流程

在这里插入图片描述

  • 真正的纹理创建流程
    SkImage_Raster::onAsView调用到GrMakeCachedBitmapProxyView先创建Bitmap,这里调用GrProxyProvider::createProxyFromBitmap->createNonMippedProxyFromBitmap,这里主要调用GrSurfaceProxyPriv::doLazyInstantiation,调用GrProxyProvider::createNonMippedProxyFromBitmap后,调用GrResourceProvider::createTexture,这里主要执行两个流程
  1. 调用GrGpu::createTextureCommon->GrGLGpu::onCreateTexture->GrGLGpu::createTexture创建纹理
  2. 调用GrGpu::createTexture->GrGpu::writePixels执行 Texture upload流程
external/skia/src/image/SkImage_Raster.cpp
#if SK_SUPPORT_GPU
std::tuple 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 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 proxy;if (mipMapped == GrMipmapped::kNo ||0 == SkMipmap::ComputeLevelCount(copyBitmap.width(), copyBitmap.height())) {// 这里调用createNonMippedProxyFromBitmap,即创建GrTextureProxyproxy = 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 GrProxyProvider::createNonMippedProxyFromBitmap(const SkBitmap& bitmap,SkBackingFit fit,SkBudgeted budgeted) {auto dims = bitmap.dimensions();ATRACE_ANDROID_FRAMEWORK_ALWAYS("GrProxyProvider::createNonMippedProxyFromBitmap");...// 这里在创建时候,回调GrTextureProxy::createTexturesk_sp 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 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;// 产生一个纹理IdGL_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(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;ifloat 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.cppvoid 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(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: 28SkSTArray<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, SkSurface::BackendSurfaceAccess, GrFlushInfo const&, GrBackendSurfaceMutableState const*)+2780)
#07 pc 0000000000536d44  /system/lib64/libhwui.so (GrDrawingManager::flushSurfaces(SkSpan, SkSurface::BackendSurfaceAccess, GrFlushInfo const&, GrBackendSurfaceMutableState const*)+164)
#08 pc 000000000052f5f0  /system/lib64/libhwui.so (GrDirectContextPriv::flushSurfaces(SkSpan, 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, std::__1::allocator > > 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, 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 static void debug_stack() {android::CallStack stack("zzh");}

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...