SurfaceFlinger学习笔记(七)之SKIA

news2024/9/29 1:30:21

关于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<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,这里主要执行两个流程

  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 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
分两种情况:

  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.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);
}
  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<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");
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/186475.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

react 实现表格列表拖拽排序

问题描述 在项目开发中&#xff0c;遇到这样一个需求&#xff1a;需要对表格里面的数据进行拖拽排序。 效果图如下所示&#xff1a; 思路 安装两个插件&#xff1a; react-sortable-hoc &#xff08;或者 react-beautiful-dnd&#xff09;array-move npm install --save r…

59 多线程环境普通变量作为标记循环不结束

前言 最近看到这篇例子的时候, [讨论] 内存可见性问题, 吧其中的 demo 拿到本地来跑 居然 和楼主一样, testBasicType 这里面的这个子线程 居然 不结束了, 卧槽 我还以为 只是可能 用的时间稍微长一点 哪知道 直接 无限期执行下去了, 然后 另外还有一个情况就是 加上了 -…

Segmenter论文解读

Segmenter: Transformer for Semantic Segmentation 论文&#xff1a;[2105.05633] Segmenter: Transformer for Semantic Segmentation (arxiv.org) 代码&#xff1a;[rstrudel/segmenter: ICCV2021] Official PyTorch implementation of Segmenter: Transformer for Semanti…

vue3+ts error TS7053:

在远程仓库拉取线上正常运行的项目&#xff0c;编译之后出现报错出现问题&#xff0c;逐步排查node版本是否与别人一致2.检查node_modules是否与别人一致检查到这一步就发现了是因为依赖版本不一致导致的原因因为目前vue-tsc等依赖更新频繁把这两个依赖的版本号锁死&#xff0c…

vuex

目录 1、什么是vuex 2、vuex的工作方式 3、vuex的使用场景 4、工作流程&#xff1a;View -> Actions -> Mutations -> State -> View 5、vuex的核心API ​ &#xff08;1&#xff09;state&#xff1a; ​ &#xff08;2&#xff09;mutations ​ &#xff…

Pinia的使用(以vue3+ts+vite为例)

文章目录Pinia1. 安装2. 引入vue33. 初始化store仓库4. 修改state5. 解构store6. store中的方法和计算属性&#xff08;actions、getters&#xff09;7. API7.1 $reset7.2 $subscribe 和 $onAction8. 插件案例&#xff1a;持久化插件Pinia Pinia官方文档 Pinia GitHub地址 1…

VSCode vscode-pandoc插件将中文Markdown转换为好看的pdf文档(使用eisvogel模板)

Markdown的使用经常需要转变其他格式&#xff0c;在VSCode里有个很好用的插件&#xff1a;vscode-pandoc&#xff0c;先进行下载。 打开设置&#xff08;左下角的小齿轮&#xff09; 输入pandoc 在HTML Opt String中粘贴入&#xff1a; -s -t html5可将Markdown转换输出HTML。…

STL-----map的常见使用

1&#xff0c;MAP的说明Map是STL的一个关联容器&#xff0c;它提供一对一&#xff08;其中第一个可以称为关键字&#xff0c;每个关键字只能在map中出现一次&#xff0c;第二个可能称为该关键字的值&#xff09;的数据 处理能力&#xff0c;由于这个特性&#xff0c;它完成有可…

3.1.8 多态

文章目录1.概念2.特点3.入门案例练习4 多态的好处5 多态的使用6 练习&#xff1a;多态成员使用测试7 拓展7.1 综合案例7.2 多态为了统一调用标准7.3 静态变量和实例变量的区别7.4 向上转型和向下转型1.概念 多态是面向对象程序设计&#xff08;OOP&#xff09;的一个重要特征&…

【数据结构初阶】第三篇——单链表

链表的概念及其结构 初始化链表 打印单链表 增加结点 头插 尾插 在给定位置之前插入 在给定位置之后插入 删除结点 头删 尾删 删除给定位置的结点 查找数据 修改数据 链表的概念及其结构 基本概念 链表是一种物理存储结构上非连续&#xff0c;非顺序的存储结构&a…

盘点保护隐私安全的浏览器,密码锁屏这个功能,真香

在互联网时代&#xff0c;大家都比较关心自己的隐私安全。一些互联网公司和在线客服会跟踪用户的在线活动&#xff0c;收集用户的个人信息&#xff0c;有时候甚至因为个人的不良习惯导致信息泄露&#xff0c;因此选择隐私和安全性好的浏览器尤其重要。下面给大家介绍隐私安全做…

大数据技术架构(组件)11——Hive:日期函数

1.4.5、日期函数1.4.5.1、from_unixtimeselect from_unixtime(1638602968),from_unixtime(1638602968,yyyy-MM-dd HH:mm:SS),from_unixtime(1638602968,yyyy-MM-dd);1.4.5.2、unix_timestampselect unix_timestamp();1.4.5.3、to_dateselect to_date(2021-12-04 2021-12-04 15:…

【授权与认证】OAuth 2.0 和 OIDC 的异同点

开发者谈 | OAuth 2.0 和 OIDC 协议的关系&#xff1f;&#xff08;内含必看案例&#xff09; 【Web 安全】CSRF 攻击详解 OAuth 2.0 OAuth 2.0 的一个简单解释OAuth 2.0 的四种方式什么是Oauth2.0&#xff0c;Oauth2.0的四种授权模式 简单说&#xff0c;OAuth 就是一种授权…

【前端】Vue项目:旅游App-(16)home+hooks:窗口滚动到底部动态加载新数据、抽取到hook

文章目录目标过程与代码监听窗口的滚动窗口上事件监听的移除封装到一个hook回调函数法&#xff08;不推荐&#xff09;返回值法&#xff08;推荐&#xff09;效果总代码修改或添加的文件hooks的useScrollhome-content参考本项目博客总结&#xff1a;【前端】Vue项目&#xff1a…

git 使用tag

文章目录概述示例创建标签 tag查看tag删除本地标签推送标签git 根据tag创建分支回退到tag参考概述 常常为发布上线某个版本打上一个标签&#xff0c;表示这是什么版本&#xff0c;这样后续找起来就很方便。 如果没有标签只能通过commit历史去查找&#xff0c;而且commit版本显…

每日学术速递1.30

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 更多Ai资讯&#xff1a; 今天带来的arXiv上最新发表的3篇文本图像的生成论文。 Subjects: cs.LG、cs.Cv、cs.AI、cs.CL 1.StyleGAN-T: Unlocking the Power of GANs for Fast Large-Scale Text-to-Im…

Spire.Doc for Java v11.1.1 Patch

Spire.Doc for Java是一个专业的 Word API&#xff0c;它使 Java 应用程序能够在不依赖 Microsoft Word 的情况下创建、转换、操作和打印 Word文档。 通过使用这个多功能库&#xff0c;开发人员能够毫不费力地处理大量任务&#xff0c;例如插入图像、超链接、 数字签名、书签和…

Mybatis-plus(下)

一&#xff0c;乐观锁可参考官方文档&#xff1a;https://baomidou.com/pages/0d93c0/场景&#xff1a;当两个工作人员同时去处理一条投诉工单的时候当两个人一起点开了投诉工单详情 并一起编辑处理 随后同时反馈给用户时 此时就会出现矛盾 当系统正常 没有bug的时候 是会出现两…

SpringCloud_Sleuth分布式链路请求跟踪

目录一、概述1.为什么会出现这个技术&#xff1f;需要解决哪些问题&#xff1f;2.是什么3.解决二、搭建链路监控步骤1.zipkin2.服务提供者3.服务消费者&#xff08;调用方&#xff09;4.依次启动eureka7001/8001/805.打开浏览器访问&#xff1a; http://localhost:9411一、概述…

网络流量监控对DMS系统排错分析案例

背景 DMS系统是某汽车集团的经销商在线系统&#xff0c;是汽车集团的重要业务系统。本次分析重点针对DMS系统性能进行分析&#xff0c;以供安全取证、性能分析、网络质量监测以及深层网络分析。 该汽车总部已部署NetInside流量分析系统&#xff0c;使用流量分析系统提供实时和…