Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c44920c3 authored by John Reck's avatar John Reck Committed by Automerger Merge Worker
Browse files

Merge "Improve picture capture debug path" into sc-dev am: b99e72f7

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14928133

Change-Id: I20ad30f1cae070e70180b2578cbd9b1227648ebe
parents 633087fe b99e72f7
Loading
Loading
Loading
Loading
+5 −7
Original line number Diff line number Diff line
@@ -953,8 +953,7 @@ public class ViewDebug {
        private final Callable<OutputStream> mCallback;
        private final Executor mExecutor;
        private final ReentrantLock mLock = new ReentrantLock(false);
        private final ArrayDeque<byte[]> mQueue = new ArrayDeque<>(3);
        private final ByteArrayOutputStream mByteStream = new ByteArrayOutputStream();
        private final ArrayDeque<Picture> mQueue = new ArrayDeque<>(3);
        private boolean mStopListening;
        private Thread mRenderThread;

@@ -990,9 +989,7 @@ public class ViewDebug {
                mQueue.removeLast();
                needsInvoke = false;
            }
            picture.writeToStream(mByteStream);
            mQueue.add(mByteStream.toByteArray());
            mByteStream.reset();
            mQueue.add(picture);
            mLock.unlock();

            if (needsInvoke) {
@@ -1003,7 +1000,7 @@ public class ViewDebug {
        @Override
        public void run() {
            mLock.lock();
            final byte[] picture = mQueue.poll();
            final Picture picture = mQueue.poll();
            final boolean isStopped = mStopListening;
            mLock.unlock();
            if (Thread.currentThread() == mRenderThread) {
@@ -1024,7 +1021,8 @@ public class ViewDebug {
            }
            if (stream != null) {
                try {
                    stream.write(picture);
                    picture.writeToStream(stream);
                    stream.flush();
                } catch (IOException ex) {
                    Log.w("ViewDebug", "Aborting rendering commands capture "
                            + "due to IOException writing to output stream", ex);
+8 −0
Original line number Diff line number Diff line
@@ -275,6 +275,14 @@ CopyResult Readback::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap
    return copyResult;
}

CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, SkBitmap* bitmap) {
    Rect srcRect;
    Matrix4 transform;
    transform.loadScale(1, -1, 1);
    transform.translate(0, -1);
    return copyImageInto(image, transform, srcRect, bitmap);
}

CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTransform,
                                   const Rect& srcRect, SkBitmap* bitmap) {
    ATRACE_CALL();
+1 −0
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ public:
    CopyResult copySurfaceInto(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap);

    CopyResult copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap);
    CopyResult copyImageInto(const sk_sp<SkImage>& image, SkBitmap* bitmap);

    CopyResult copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);

+2 −1
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ class Picture {
public:
    explicit Picture(const Picture* src = NULL);
    explicit Picture(sk_sp<SkPicture>&& src);
    virtual ~Picture() = default;

    Canvas* beginRecording(int width, int height);

@@ -49,7 +50,7 @@ public:

    static Picture* CreateFromStream(SkStream* stream);

    void serialize(SkWStream* stream) const;
    virtual void serialize(SkWStream* stream) const;

    void draw(Canvas* canvas);

+109 −2
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@
#include <Picture.h>
#include <Properties.h>
#include <RootRenderNode.h>
#include <SkImagePriv.h>
#include <SkSerialProcs.h>
#include <dlfcn.h>
#include <gui/TraceUtils.h>
#include <inttypes.h>
@@ -35,6 +37,7 @@
#include <renderthread/RenderProxy.h>
#include <renderthread/RenderTask.h>
#include <renderthread/RenderThread.h>
#include <src/image/SkImage_Base.h>
#include <thread/CommonPool.h>
#include <utils/Color.h>
#include <utils/RefBase.h>
@@ -497,6 +500,108 @@ private:
    jobject mObject;
};

using TextureMap = std::unordered_map<uint32_t, sk_sp<SkImage>>;

struct PictureCaptureState {
    // Each frame we move from the active map to the previous map, essentially an LRU of 1 frame
    // This avoids repeated readbacks of the same image, but avoids artificially extending the
    // lifetime of any particular image.
    TextureMap mActiveMap;
    TextureMap mPreviousActiveMap;
};

// TODO: This & Multi-SKP & Single-SKP should all be de-duped into
// a single "make a SkPicture serailizable-safe" utility somewhere
class PictureWrapper : public Picture {
public:
    PictureWrapper(sk_sp<SkPicture>&& src, const std::shared_ptr<PictureCaptureState>& state)
            : Picture(), mPicture(std::move(src)) {
        ATRACE_NAME("Preparing SKP for capture");
        // Move the active to previous active
        state->mPreviousActiveMap = std::move(state->mActiveMap);
        state->mActiveMap.clear();
        SkSerialProcs tempProc;
        tempProc.fImageCtx = state.get();
        tempProc.fImageProc = collectNonTextureImagesProc;
        auto ns = SkNullWStream();
        mPicture->serialize(&ns, &tempProc);
        state->mPreviousActiveMap.clear();

        // Now snapshot a copy of the active map so this PictureWrapper becomes self-sufficient
        mTextureMap = state->mActiveMap;
    }

    static sk_sp<SkImage> imageForCache(SkImage* img) {
        const SkBitmap* bitmap = as_IB(img)->onPeekBitmap();
        // This is a mutable bitmap pretending to be an immutable SkImage. As we're going to
        // actually cross thread boundaries here, make a copy so it's immutable proper
        if (bitmap && !bitmap->isImmutable()) {
            ATRACE_NAME("Copying mutable bitmap");
            return SkImage::MakeFromBitmap(*bitmap);
        }
        if (img->isTextureBacked()) {
            ATRACE_NAME("Readback of texture image");
            return img->makeNonTextureImage();
        }
        SkPixmap pm;
        if (img->isLazyGenerated() && !img->peekPixels(&pm)) {
            ATRACE_NAME("Readback of HW bitmap");
            // This is a hardware bitmap probably
            SkBitmap bm;
            if (!bm.tryAllocPixels(img->imageInfo())) {
                // Failed to allocate, just see what happens
                return sk_ref_sp(img);
            }
            if (RenderProxy::copyImageInto(sk_ref_sp(img), &bm)) {
                // Failed to readback
                return sk_ref_sp(img);
            }
            bm.setImmutable();
            return SkMakeImageFromRasterBitmap(bm, kNever_SkCopyPixelsMode);
        }
        return sk_ref_sp(img);
    }

    static sk_sp<SkData> collectNonTextureImagesProc(SkImage* img, void* ctx) {
        PictureCaptureState* context = reinterpret_cast<PictureCaptureState*>(ctx);
        const uint32_t originalId = img->uniqueID();
        auto it = context->mActiveMap.find(originalId);
        if (it == context->mActiveMap.end()) {
            auto pit = context->mPreviousActiveMap.find(originalId);
            if (pit == context->mPreviousActiveMap.end()) {
                context->mActiveMap[originalId] = imageForCache(img);
            } else {
                context->mActiveMap[originalId] = pit->second;
            }
        }
        return SkData::MakeEmpty();
    }

    static sk_sp<SkData> serializeImage(SkImage* img, void* ctx) {
        PictureWrapper* context = reinterpret_cast<PictureWrapper*>(ctx);
        const uint32_t id = img->uniqueID();
        auto iter = context->mTextureMap.find(id);
        if (iter != context->mTextureMap.end()) {
            img = iter->second.get();
        }
        return img->encodeToData();
    }

    void serialize(SkWStream* stream) const override {
        SkSerialProcs procs;
        procs.fImageProc = serializeImage;
        procs.fImageCtx = const_cast<PictureWrapper*>(this);
        procs.fTypefaceProc = [](SkTypeface* tf, void* ctx) {
            return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
        };
        mPicture->serialize(stream, &procs);
    }

private:
    sk_sp<SkPicture> mPicture;
    TextureMap mTextureMap;
};

static void android_view_ThreadedRenderer_setPictureCapturedCallbackJNI(JNIEnv* env,
        jobject clazz, jlong proxyPtr, jobject pictureCallback) {
    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
@@ -507,9 +612,11 @@ static void android_view_ThreadedRenderer_setPictureCapturedCallbackJNI(JNIEnv*
        LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
        auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(vm,
                env->NewGlobalRef(pictureCallback));
        proxy->setPictureCapturedCallback([globalCallbackRef](sk_sp<SkPicture>&& picture) {
        auto pictureState = std::make_shared<PictureCaptureState>();
        proxy->setPictureCapturedCallback([globalCallbackRef,
                                           pictureState](sk_sp<SkPicture>&& picture) {
            JNIEnv* env = getenv(globalCallbackRef->vm());
            Picture* wrapper = new Picture{std::move(picture)};
            Picture* wrapper = new PictureWrapper{std::move(picture), pictureState};
            env->CallStaticVoidMethod(gHardwareRenderer.clazz,
                    gHardwareRenderer.invokePictureCapturedCallback,
                    static_cast<jlong>(reinterpret_cast<intptr_t>(wrapper)),
Loading