Loading core/java/android/view/ViewDebug.java +5 −7 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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) { Loading @@ -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); Loading libs/hwui/Readback.cpp +8 −0 Original line number Diff line number Diff line Loading @@ -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(); Loading libs/hwui/Readback.h +1 −0 Original line number Diff line number Diff line Loading @@ -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); Loading libs/hwui/jni/Picture.h +2 −1 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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); Loading libs/hwui/jni/android_graphics_HardwareRenderer.cpp +109 −2 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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> Loading Loading @@ -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); Loading @@ -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 Loading
core/java/android/view/ViewDebug.java +5 −7 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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) { Loading @@ -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); Loading
libs/hwui/Readback.cpp +8 −0 Original line number Diff line number Diff line Loading @@ -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(); Loading
libs/hwui/Readback.h +1 −0 Original line number Diff line number Diff line Loading @@ -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); Loading
libs/hwui/jni/Picture.h +2 −1 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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); Loading
libs/hwui/jni/android_graphics_HardwareRenderer.cpp +109 −2 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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> Loading Loading @@ -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); Loading @@ -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