Drawing system of Android Q skia

Time:2022-5-13

Lifelong learning with you, this is Android programmer

Classic good articles are recommended. By reading this article, you will gain the following knowledge points:

1、 Drawing mechanism of view
2、 Hwui drawing architecture
3、 Render device
4、 ImageView drawing
5、 Geometric treatment during drawing skmatrix

The biggest change in Android Q rendering system is to add skia’s part. Before Android P rendering, it was directly encapsulated in hwui, encapsulated in OP, and directly transferred to GPU; Now in Android Q, in addition to the encapsulation in hwui, there is one in hwuiDisplayListIn addition, it is encapsulated again in skia, and there is another one in skiaGrOpList

Let’s have a look at the specific details

1、 Drawing mechanism of view

First, let’s take a look at the drawing mechanism of view! A general term for view. I believe many students have customized the view ~ do we have to rewrite a custom viewonDrawMethod, but when was the OnDraw method adjusted! Let’s have a look!

Drawing system of Android Q skia

Let’s look at the key points:

  • ChoreographerChoreographer
    Main processingDisplayEventReceiverReceivedVsyncSignal, control the beat of drawing, and synchronize with the bottom display!
  • ViewRootImpl
    The root of all views in a window. All views are organized according to the data structure tree
  • ThreadedRenderer
    Threaded renderer, which will encapsulate rendering threads. Hardware acceleration mainly goes here. In the early days, there was no hardware accelerationdrawSoftware, it is still reserved for compatibility only!
  • A view corresponds to a render node rendernode, which fully embodies the concept of tree structure!
  • Canvas
    Commonly known as canvas, what kind of canvas you provide has what kind of ability, and the way of rendering is different!

Let’s take the rendering of ImageView as an example to see its call stack!

12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.graphics.BaseRecordingCanvas.drawBitmap(BaseRecordingCanvas.java:88)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.java:548)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.widget.ImageView.onDraw(ImageView.java:1434)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at com.android.example.cropper.CropImageView.onDraw(CropImageView.java:167)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.view.View.draw(View.java:21594)
... ...
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4231)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.view.View.draw(View.java:21601)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at com.android.internal.policy.DecorView.draw(DecorView.java:831)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.view.View.updateDisplayListIfDirty(View.java:20437)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:575)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:581)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:654)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.view.ViewRootImpl.draw(ViewRootImpl.java:3828)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:3619)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2939)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1849)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8013)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.view.Choreographer$CallbackRecord.run(Choreographer.java:969)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.view.Choreographer.doCallbacks(Choreographer.java:793)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.view.Choreographer.doFrame(Choreographer.java:728)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:954)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.os.Handler.handleCallback(Handler.java:883)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.os.Handler.dispatchMessage(Handler.java:100)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.os.Looper.loop(Looper.java:224)
12-30 13:45:51.476 10246 26422 26422 E AndroidRuntime:  at android.app.ActivityThread.main(ActivityThread.java:7509)

In this stack, we hide some of the tree calls of view! It shows the calling process from receiving Vsync to drawing!

2、 Hwui drawing architecture

Previously, after the painting operation was encapsulated in hwui, it was directly sent to GPU for rendering and encapsulated through OpenGL or Vulkan! After the Q version, it is different. It is encapsulated through skia, then encapsulated through OpenGL or Vulkan, and finally rendered to GPU.

Drawing system of Android Q skia

The role and usefulness of skia have been significantly strengthened!
Through code analysis, skia is referenced by hwui in the form of static library!

The difference between the hwui process and the previous one is mainly reflected in the secondary encapsulation of skia ~ let’s take a look at the hwui drawing process!

Drawing system of Android Q skia

  • Skiaopenglpipeline and skiavulkanpipeline are the encapsulation of OpenGL and Vulkan respectively!
  • The code of skia is divided into two parts. In this figure, it is mainly in hwui, and another part is in skia’s independent static library!
  • Skia has corresponding structure and hwui matching!

Let’s take a brief look at a calling process of skiacanvas:

01-02 17:19:52.510 31741 32145 D xm-gfx-skia: SkCanvas: #00 pc 00000000002d9750  /system/lib64/libhwui.so (SkCanvas::init(sk_sp<SkBaseDevice>)+416)
01-02 17:19:52.510 31741 32145 D xm-gfx-skia: SkCanvas: #01 pc 00000000002d94d8  /system/lib64/libhwui.so (SkCanvas::SkCanvas(sk_sp<SkBaseDevice>)+192)
01-02 17:19:52.510 31741 32145 D xm-gfx-skia: SkCanvas: #02 pc 00000000002d9370  /system/lib64/libhwui.so (SkSurface_Gpu::onNewCanvas()+88)
01-02 17:19:52.510 31741 32145 D xm-gfx-skia: SkCanvas: #03 pc 00000000002770fc  /system/lib64/libhwui.so (SkSurface::getCanvas()+36)
01-02 17:19:52.510 31741 32145 D xm-gfx-skia: SkCanvas: #04 pc 0000000000276ee4  /system/lib64/libhwui.so (android::uirenderer::skiapipeline::SkiaPipeline::tryCapture(SkSurface*)+48)
01-02 17:19:52.510 31741 32145 D xm-gfx-skia: SkCanvas: #05 pc 0000000000276be8  /system/lib64/libhwui.so (android::uirenderer::skiapipeline::SkiaPipeline::renderFrame(android::uirenderer::LayerUpdateQueue const&, SkRect const&, ... ...

The drawing operation is mainly carried out in skiapiline:

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) {
    bool previousSkpEnabled = Properties::skpCaptureEnabled;
    if (mPictureCapturedCallback) {
        Properties::skpCaptureEnabled = true;
    }

    // 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());

    if (CC_UNLIKELY(Properties::debugOverdraw)) {
        renderOverdraw(clip, nodes, contentDrawBounds, surface, preTransform);
    }

    ATRACE_NAME("flush commands");
    surface->getCanvas()->flush();

    Properties::skpCaptureEnabled = previousSkpEnabled;
}

  • Trycapture screenshots are the same. They are generally not opened and can be used for debugging
  • Renderlayersimpl renders the front end of the layer to be rendered first
  • Renderframeimpl backend, draw to oplist.
  • Flush this is the real drawing to buffer

3、 Render device

Skia defines all kinds of devices you draw and realizes their drawing functions! Let’s take a look at the device related classes!

Drawing system of Android Q skia

More commonly usedSkBitmapDeviceandSkGpuDevice~ other application scenarios are different! Use nowSkGpuDeviceMore.

GPU related code location:

external/skia/src/gpu

4、 ImageView drawing

Let’s continue to see the drawing of ImageView! The front stack has been adjustedBaseRecordingCanvas.drawBitmap。 Canvas is originally skiarecordingcanvas.

Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::RenderNode* renderNode) {
    return new uirenderer::skiapipeline::SkiaRecordingCanvas(renderNode, width, height);
}

4.1 recording operation

So the drawbitmap of Java layer will be adjusted toSkiaRecordingCanvasLet’s take a look at this function and draw a bitmap from a certain position.

void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
    sk_sp<SkImage> image = bitmap.makeImage();

    applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) {
        mRecorder.drawImage(image, left + x, top + y, &p, bitmap.palette());
    });

    // if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means
    // it is not safe to store a raw SkImage pointer, because the image object will be destroyed
    // when this function ends.
    if (!bitmap.isImmutable() && image.get() && !image->unique()) {
        mDisplayList->mMutableImages.push_back(image.get());
    }
}

mRecorderyesRecordingCanvas, FDL isDisplayListDataObject.

void RecordingCanvas::drawImage(const sk_sp<SkImage>& image, SkScalar x, SkScalar y,
                                const SkPaint* paint, BitmapPalette palette) {
    fDL->drawImage(image, x, y, paint, palette);
}

Finally, the drawing of ImageView will be converted to DrawImage!

void DisplayListData::drawImage(sk_sp<const SkImage> image, SkScalar x, SkScalar y,
                                const SkPaint* paint, BitmapPalette palette) {
    this->push<DrawImage>(0, std::move(image), x, y, paint, palette);
}

This push function implements how our corresponding drawing operation OP is stored ~ let’s see ~

template <typename T, typename... Args>
void* DisplayListData::push(size_t pod, Args&&... args) {
    size_t skip = SkAlignPtr(sizeof(T) + pod);
    SkASSERT(skip < (1 << 24));
    if (fUsed + skip > fReserved) {
        static_assert(SkIsPow2(SKLITEDL_PAGE), "This math needs updating for non-pow2.");
        // Next greater multiple of SKLITEDL_PAGE.
        fReserved = (fUsed + skip + SKLITEDL_PAGE) & ~(SKLITEDL_PAGE - 1);
        fBytes.realloc(fReserved);
    }
    SkASSERT(fUsed + skip <= fReserved);
    auto op = (T*)(fBytes.get() + fUsed);
    fUsed += skip;
    new (op) T{std::forward<Args>(args)...};
    op->type = (uint32_t)T::kType;
    op->skip = skip;
    return op + 1;
}

  • Base addressfBytes, the displaylistdata starts to store the address of the OP, which is pre allocated and continuously expanded in the future.
  • Used, how much memory is used in the allocated memory.
  • Skip the memory size occupied by the newly added Op.
  • Pay attention hereauto op = (T*)(fBytes.get() + fUsed);From this address, force a t object.
  • Pay attention to this methodnew (op) T{std::forward<Args>(args)...};Build the corresponding OP whose address is forced from the front.

Definition of OP:

struct Op {
    uint32_t type : 8;
    uint32_t skip : 24;
};

  • Type indicates the type of OP, and skip indicates the size!
  • The type of DrawImage isType::DrawImage
  • The specific OP derived from OP implements the draw method to complete the real drawing of the modified Op.

So far, the operation of drawing ImageView has been converted toDrawImageDrawing operation, saved toDisplayListDataYes! No further action!

4.1 rendering operation op

In 2020, everything should start with a bat, but the rendering of OP here should start fromHardwareRendererofsyncAndDrawFrameSpeaking of, the huwi stability on P has been said before. It is inDrawFrameTaskImplemented in.

It’ll be transferred hereCanvasContextMediumprepareTreeanddraw

  • Preparetree transfers the previously recorded data of displaylistdata to themLayerUpdateQueuein
  • Draw to draw the data obtained in mlayerupdatequeue, which is what I said earlierSkiaPipeline::renderFrameFunction.

FinallyDisplayListDataIn,

void DisplayListData::draw(SkCanvas* canvas) const {
    SkAutoCanvasRestore acr(canvas, false);
    this->map(draw_fns, canvas, canvas->getTotalMatrix());
}

Map function:

template <typename Fn, typename... Args>
inline void DisplayListData::map(const Fn fns[], Args... args) const {
    auto end = fBytes.get() + fUsed;
    for (const uint8_t* ptr = fBytes.get(); ptr < end;) {
        auto op = (const Op*)ptr;
        auto type = op->type;
        auto skip = op->skip;
        if (auto fn = fns[type]) {  // We replace no-op functions with nullptrs
            fn(op, args...);        // to avoid the overhead of a pointless call.
        }
        ptr += skip;
    }
}

According to the type, find the corresponding FN and execute it!

#define X(T)                                                    \
    [](const void* op, SkCanvas* c, const SkMatrix& original) { \
        ((const T*)op)->draw(c, original);                      \
    },
static const draw_fn draw_fns[] = {
#include "DisplayListOps.in"
};
#undef X

Execute the draw function of the corresponding t! We converted ImageView to DrawImage

struct DrawImage final : Op {
    static const auto kType = Type::DrawImage;
    DrawImage(sk_sp<const SkImage>&& image, SkScalar x, SkScalar y, const SkPaint* paint,
              BitmapPalette palette)
            : image(std::move(image)), x(x), y(y), palette(palette) {
        if (paint) {
            this->paint = *paint;
        }
    }
    sk_sp<const SkImage> image;
    SkScalar x, y;
    SkPaint paint;
    BitmapPalette palette;
    void draw(SkCanvas* c, const SkMatrix&) const { c->drawImage(image.get(), x, y, &paint); }
};

The DrawImage inside is canvas’s!

void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
    TRACE_EVENT0("skia", TRACE_FUNC);
    RETURN_ON_NULL(image);
    this->onDrawImage(image, x, y, paint);
}

Ok! Come hereonDrawImageMethod, you need to distinguish between specific devices.

Skia treatment

Here we start with a stack. DrawImage will be provided to GPU in the form of texture for processing. Here is the intermediate process.

01-03 17:37:41.895 20612 20680 D GrGLGpu: #00 pc 00257b89  /system/lib/libhwui.so (GrGLGpu::onCreateTexture(GrSurfaceDesc const&, SkBudgeted, GrMipLevel const*, int)+88)
01-03 17:37:41.895 20612 20680 D GrGLGpu: #01 pc 0024532f  /system/lib/libhwui.so (GrGpu::createTexture(GrSurfaceDesc const&, SkBudgeted, GrMipLevel const*, int)+126)
01-03 17:37:41.895 20612 20680 D GrGLGpu: #02 pc 0023abbb  /system/lib/libhwui.so (GrResourceProvider::createTexture(GrSurfaceDesc const&, SkBudgeted, GrResourceProvider::Flags)+86)
01-03 17:37:41.895 20612 20680 D GrGLGpu: #03 pc 0023a8fb  /system/lib/libhwui.so (GrResourceProvider::createTexture(GrSurfaceDesc const&, SkBudgeted, SkBackingFit, GrMipLevel const&, GrResourceProvider::Flags)+110)
01-03 17:37:41.895 20612 20680 D GrGLGpu: #04 pc 0023a795  /system/lib/libhwui.so (_ZNSt3__110__function6__funcIZN15GrProxyProvider18createTextureProxyE5sk_spI7SkImageE14GrSurfaceFlagsi10SkBudgeted12SkBackingFit22GrInternalSurfaceFlagsE3$_0NS_9allocatorISA_EEFS3_I9GrSurfaceEP18GrResourceProviderEEclEOSG_$955cb6000423a36286866a405c65dcb6+84)
01-03 17:37:41.895 20612 20680 D GrGLGpu: #05 pc 001b907f  /system/lib/libhwui.so (GrSurfaceProxyPriv::doLazyInstantiation(GrResourceProvider*)+70)
01-03 17:37:41.895 20612 20680 D GrGLGpu: #06 pc 0026ebe7  /system/lib/libhwui.so (GrProxyProvider::createTextureProxy(sk_sp<SkImage>, GrSurfaceFlags, int, SkBudgeted, SkBackingFit, GrInternalSurfaceFlags)+762)
01-03 17:37:41.895 20612 20680 D GrGLGpu: #07 pc 0026e84d  /system/lib/libhwui.so (GrUploadBitmapToTextureProxy(GrProxyProvider*, SkBitmap const&)+132)
01-03 17:37:41.895 20612 20680 D GrGLGpu: #08 pc 0012ba95  /system/lib/libhwui.so (GrBitmapTextureMaker::refOriginalTextureProxy(bool, GrTextureMaker::AllowedTexGenType)+184)
01-03 17:37:41.895 20612 20680 D GrGLGpu: #09 pc 001e6c19  /system/lib/libhwui.so (GrTextureMaker::onRefTextureProxyForParams(GrSamplerState const&, bool, float*)+104)
01-03 17:37:41.895 20612 20680 D GrGLGpu: #10 pc 001c586f  /system/lib/libhwui.so (GrTextureProducer::refTextureProxyForParams(GrSamplerState const&, float*)+34)
01-03 17:37:41.895 20612 20680 D GrGLGpu: #11 pc 00393011  /system/lib/libhwui.so (_ZN12_GLOBAL__N_121draw_texture_producerEP9GrContextP21GrRenderTargetContextRK6GrClipRK8SkMatrixRK7SkPaintP17GrTextureProducerRK6SkRectSH_PK7SkPointS9_4GrAA13GrQuadAAFlagsN8SkCanvas17SrcRectConstraintEb.llvm.12141512145445924752+112)
01-03 17:37:41.895 20612 20680 D GrGLGpu: #12 pc 00391e5d  /system/lib/libhwui.so (SkGpuDevice::drawImageQuad(SkImage const*, SkRect const*, SkRect const*, SkPoint const*, GrAA, GrQuadAAFlags, SkMatrix const*, SkPaint const&, SkCanvas::SrcRectConstraint)+3404)
01-03 17:37:41.895 20612 20680 D GrGLGpu: #13 pc 00132a29  /system/lib/libhwui.so (SkGpuDevice::drawImageRect(SkImage const*, SkRect const*, SkRect const&, SkPaint const&, SkCanvas::SrcRectConstraint)+108)
01-03 17:37:41.895 20612 20680 D GrGLGpu: #14 pc 001e254d  /system/lib/libhwui.so (SkCanvas::onDrawImage(SkImage const*, float, float, SkPaint const*)+540)
01-03 17:37:41.895 20612 20680 D GrGLGpu: #15 pc 0027a18b  /system/lib/libhwui.so (SkCanvas::drawImage(SkImage const*, float, float, SkPaint const*)+138)

DrawImage is processed by texture in skia, and will also be secondary encapsulated as op textureop of GPU.

void GrRenderTargetContext::drawTexturedQuad(const GrClip& clip,
                                             GrSurfaceProxyView proxyView,
                                             SkAlphaType srcAlphaType,
                                             sk_sp<GrColorSpaceXform> textureXform,
                                             GrSamplerState::Filter filter,
                                             const SkPMColor4f& color,
                                             SkBlendMode blendMode,
                                             GrAA aa,
                                             DrawQuad* quad,
                                             const SkRect* domain) {
    ... ...
    if (opt != QuadOptimization::kDiscarded) {
        ... ...
        this->addDrawOp(finalClip,
                        GrTextureOp::Make(fContext, std::move(proxyView), srcAlphaType,
                                          std::move(textureXform), filter, color, saturate,
                                          blendMode, aaType, quad, domain));
    }
}

Finally added toGrOpsTaskMedium:

    void addDrawOp(std::unique_ptr<GrDrawOp> op, const GrProcessorSet::Analysis& processorAnalysis,
                   GrAppliedClip&& clip, const DstProxyView& dstProxyView,
                   GrTextureResolveManager textureResolveManager, const GrCaps& caps) {
        auto addDependency = [ textureResolveManager, &caps, this ] (
                GrSurfaceProxy* p, GrMipMapped mipmapped) {
            this->addSampledTexture(p);
            this->addDependency(p, mipmapped, textureResolveManager, caps);
        };

        op->visitProxies(addDependency);
        clip.visitProxies(addDependency);
        if (dstProxyView.proxy()) {
            this->addSampledTexture(dstProxyView.proxy());
            addDependency(dstProxyView.proxy(), GrMipMapped::kNo);
        }

        this->recordOp(std::move(op), processorAnalysis, clip.doesClip() ? &clip : nullptr,
                       &dstProxyView, caps);
    }

In recordop, it will be added to fopchains, a data structure of skstarray!

4.4 GPU processing

The real process of drawing is in the flush function:

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) {
        ... ...
    renderFrameImpl(clip, nodes, opaque, contentDrawBounds, canvas, preTransform);
    ... ...
    surface->getCanvas()->flush();

    Properties::skpCaptureEnabled = previousSkpEnabled;
}

Textureop is processed in the onexecute function:

    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
        auto pipelineFlags = (GrAAType::kMSAA == fMetadata.aaType())
                ? GrPipeline::InputFlags::kHWAntialias
                : GrPipeline::InputFlags::kNone;

        auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(flushState,
                                                                 GrProcessorSet::MakeEmptySet(),
                                                                 pipelineFlags);

        flushState->executeDrawsAndUploadsForMeshDrawOp(this, chainBounds, pipeline);
    }

When textureop is in flush, texture will be used by GPU:

01-06 16:32:47.839 19995 20045 D GrGLGpu: #00 pc 001cc4af  /system/lib/libhwui.so (GrGLGpu::bindTexture(int, GrSamplerState, GrGLTexture*)+46)
01-06 16:32:47.839 19995 20045 D GrGLGpu: #01 pc 001c9fa1  /system/lib/libhwui.so (GrGLProgram::updatePrimitiveProcessorTextureBindings(GrPrimitiveProcessor const&, GrTextureProxy const* const*)+68)
01-06 16:32:47.839 19995 20045 D GrGLGpu: #02 pc 001c8f67  /system/lib/libhwui.so (GrGLGpu::draw(GrRenderTarget*, GrSurfaceOrigin, GrPrimitiveProcessor const&, GrPipeline const&, GrPipeline::FixedDynamicState const*, GrPipeline::DynamicStateArrays const*, GrMesh const*, int)+542)
01-06 16:32:47.839 19995 20045 D GrGLGpu: #03 pc 001c8d3f  /system/lib/libhwui.so (GrGLGpuRTCommandBuffer::onDraw(GrPrimitiveProcessor const&, GrPipeline const&, GrPipeline::FixedDynamicState const*, GrPipeline::DynamicStateArrays const*, GrMesh const*, int, SkRect const&)+30)
01-06 16:32:47.839 19995 20045 D GrGLGpu: #04 pc 0018ab83  /system/lib/libhwui.so (GrGpuRTCommandBuffer::draw(GrPrimitiveProcessor const&, GrPipeline const&, GrPipeline::FixedDynamicState const*, GrPipeline::DynamicStateArrays const*, GrMesh const*, int, SkRect const&)+166)
01-06 16:32:47.839 19995 20045 D GrGLGpu: #05 pc 0018a8e3  /system/lib/libhwui.so (GrOpFlushState::executeDrawsAndUploadsForMeshDrawOp(GrOp const*, SkRect const&, GrProcessorSet&&, unsigned int, GrUserStencilSettings const*)+310)
01-06 16:32:47.839 19995 20045 D GrGLGpu: #06 pc 003de943  /system/lib/libhwui.so (_ZN12_GLOBAL__N_19TextureOp9onExecuteEP14GrOpFlushStateRK6SkRect$ab67424b01b2ae857b75443a16f18610+66)
01-06 16:32:47.839 19995 20045 D GrGLGpu: #07 pc 001b8cef  /system/lib/libhwui.so (GrOp::execute(GrOpFlushState*, SkRect const&)+50)
01-06 16:32:47.839 19995 20045 D GrGLGpu: #08 pc 001b8b4f  /system/lib/libhwui.so (GrRenderTargetOpList::onExecute(GrOpFlushState*)+282)
01-06 16:32:47.840 19995 20045 D GrGLGpu: #09 pc 001fce6b  /system/lib/libhwui.so (GrDrawingManager::flush(GrSurfaceProxy*, SkSurface::BackendSurfaceAccess, GrFlushFlags, int, GrBackendSemaphore*, void (*)(void*), void*)+1086)
01-06 16:32:47.840 19995 20045 D GrGLGpu: #10 pc 001fc9a1  /system/lib/libhwui.so (GrDrawingManager::prepareSurfaceForExternalIO(GrSurfaceProxy*, SkSurface::BackendSurfaceAccess, GrFlushFlags, int, GrBackendSemaphore*, void (*)(void*), void*)+128)
01-06 16:32:47.840 19995 20045 D GrGLGpu: #11 pc 001fc86b  /system/lib/libhwui.so (GrRenderTargetContext::prepareForExternalIO(SkSurface::BackendSurfaceAccess, GrFlushFlags, int, GrBackendSemaphore*, void (*)(void*), void*)+86)
01-06 16:32:47.840 19995 20045 D GrGLGpu: #12 pc 001fc80b  /system/lib/libhwui.so (SkGpuDevice::flush()+22)

GPU processing of texture is concentrated in the bindtexture function of grglgpu.!

The whole drawing operation is completed in grglgpu:: draw. Call the toelement indexes function in the toggles.

Texture operation, you can see my another blog postOpenGLES native PNG image mapping

Drawing system of Android Q skia

4.5 GL_ Call and GPU

GL_ Call is a macro definition

#define GL_CALL(X) GR_GL_CALL(this->glGpu()->glInterface(), X)

This is the encapsulation of OpenGLES. In the pipeline of hwui, we decide what interface to use.

RenderThread::requireGlContext()
RenderThread::requireVkContext()

5、 Geometric treatment during drawing skmatrix

The rotation, scaling, offset, etc. during drawing are realized throughSkMatrixTo achieve. We may not have paid attention to it, but how and where our pictures are drawn has a lot to do with skmatrix.
For example, a picture will be created as the corresponding texture, but it is likely that the picture is only displayed in a small area on the screen. This requires setting corresponding parameters in skmatrix for scaling.

This isSkMatrixMeaning of each parameter in:

| scaleX  skewX transX |
|  skewY scaleY transY |
|  pers0  pers1  pers2 |

Let’s see what we can do:

  • Scaling: scaleX and Scaley are the scaling in the horizontal and vertical directions respectively. 1.0F is the original size!
  • Offset: transx and transy are offset in horizontal and vertical directions respectively. 0.0F is the original position!
  • Skew: skewx and skewy are the offsets in the horizontal and vertical directions respectively. 0.0F is the original position!
  • Perspective: perspective, pers0 and pers1 represent the coordinates XY of perspective respectively, and pers2 represents the scaling factor.
  • Rotation: directly set the rotation angle through setrotate function!

Let’s have a rough understanding of these operations through a diagram.

Drawing system of Android Q skia

It should be noted that some operations are mutually exclusive and cannot be performed at the same time!

How to distinguish? Skmatrix is distinguished by typemask!

    enum TypeMask {
        kIdentity_Mask    = 0,    //!< identity SkMatrix; all bits clear
        kTranslate_Mask   = 0x01, //!< translation SkMatrix
        kScale_Mask       = 0x02, //!< scale SkMatrix
        kAffine_Mask      = 0x04, //!< skew or rotate SkMatrix
        kPerspective_Mask = 0x08, //!< perspective SkMatrix
    };

Summary

This paper mainly starts with the rendering of ImageView, and analyzes a rendering process in Android Q, which is a bit of a tiger’s head and a snake’s tail. Let’s improve it later!

Reference link:https://www.jianshu.com/p/198701520cd7

So far, this article has ended. Reprint the articles on the Internet. Xiaobian feels very excellent. You are welcome to click to read the original text and support the original author. If there is infringement, please contact Xiaobian to delete it. Your suggestions and corrections are welcome. At the same time, we look forward to your attention. Thank you for reading. Thank you!