diff options
Diffstat (limited to 'libs/hwui')
-rw-r--r-- | libs/hwui/Android.bp | 9 | ||||
-rw-r--r-- | libs/hwui/Readback.cpp | 5 | ||||
-rw-r--r-- | libs/hwui/SkiaCanvas.cpp | 6 | ||||
-rw-r--r-- | libs/hwui/apex/android_bitmap.cpp | 5 | ||||
-rw-r--r-- | libs/hwui/hwui/ImageDecoder.cpp | 9 | ||||
-rw-r--r-- | libs/hwui/jni/AnimatedImageDrawable.cpp | 2 | ||||
-rwxr-xr-x | libs/hwui/jni/Bitmap.cpp | 15 | ||||
-rw-r--r-- | libs/hwui/jni/ByteBufferStreamAdaptor.cpp | 20 | ||||
-rw-r--r-- | libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp | 4 | ||||
-rw-r--r-- | libs/hwui/jni/GraphicsStatsService.cpp | 10 | ||||
-rw-r--r-- | libs/hwui/jni/ImageDecoder.cpp | 3 | ||||
-rw-r--r-- | libs/hwui/jni/Utils.cpp | 10 | ||||
-rw-r--r-- | libs/hwui/jni/Utils.h | 10 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/SkiaPipeline.cpp | 2 | ||||
-rw-r--r-- | libs/hwui/renderthread/CanvasContext.cpp | 14 | ||||
-rw-r--r-- | libs/hwui/tests/unit/ABitmapTests.cpp | 46 | ||||
-rw-r--r-- | libs/hwui/tests/unit/SkiaPipelineTests.cpp | 37 |
17 files changed, 151 insertions, 56 deletions
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index ac2fd98248d0..aa842ff6a7b7 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -171,7 +171,6 @@ cc_library_headers { cc_defaults { name: "android_graphics_apex", - host_supported: true, cflags: [ "-Wno-unused-parameter", "-Wno-non-virtual-dtor", @@ -231,7 +230,6 @@ cc_library_headers { cc_defaults { name: "android_graphics_jni", - host_supported: true, cflags: [ "-Wno-unused-parameter", "-Wno-non-virtual-dtor", @@ -537,7 +535,11 @@ cc_defaults { cc_test { name: "hwui_unit_tests", - defaults: ["hwui_test_defaults"], + defaults: [ + "hwui_test_defaults", + "android_graphics_apex", + "android_graphics_jni", + ], static_libs: [ "libgmock", @@ -549,6 +551,7 @@ cc_test { srcs: [ "tests/unit/main.cpp", + "tests/unit/ABitmapTests.cpp", "tests/unit/CacheManagerTests.cpp", "tests/unit/CanvasContextTests.cpp", "tests/unit/CommonPoolTests.cpp", diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp index 84c07d7d9dff..39900e65cb8a 100644 --- a/libs/hwui/Readback.cpp +++ b/libs/hwui/Readback.cpp @@ -146,12 +146,11 @@ CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTran } Layer layer(mRenderThread.renderState(), nullptr, 255, SkBlendMode::kSrc); - bool disableFilter = MathUtils::areEqual(skiaSrcRect.width(), skiaDestRect.width()) && - MathUtils::areEqual(skiaSrcRect.height(), skiaDestRect.height()); - layer.setForceFilter(!disableFilter); layer.setSize(displayedWidth, displayedHeight); texTransform.copyTo(layer.getTexTransform()); layer.setImage(image); + // Scaling filter is not explicitly set here, because it is done inside copyLayerInfo + // after checking the necessity based on the src/dest rect size and the transformation. if (copyLayerInto(&layer, &skiaSrcRect, &skiaDestRect, bitmap)) { copyResult = CopyResult::Success; } diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 5790150a3425..941437998838 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -789,9 +789,11 @@ void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, xform[i - start].fTx = pos.x() - tan.y() * y - halfWidth * tan.x(); xform[i - start].fTy = pos.y() + tan.x() * y - halfWidth * tan.y(); } - auto* finalCanvas = this->asSkCanvas(); + + sk_sp<SkTextBlob> textBlob(builder.make()); + apply_looper(&paintCopy, [&](const SkPaint& p) { - finalCanvas->drawTextBlob(builder.make(), 0, 0, paintCopy); + mCanvas->drawTextBlob(textBlob, 0, 0, p); }); } diff --git a/libs/hwui/apex/android_bitmap.cpp b/libs/hwui/apex/android_bitmap.cpp index b56a619b00aa..3780ba072308 100644 --- a/libs/hwui/apex/android_bitmap.cpp +++ b/libs/hwui/apex/android_bitmap.cpp @@ -163,10 +163,9 @@ jobject ABitmapConfig_getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat forma void ABitmap_notifyPixelsChanged(ABitmap* bitmapHandle) { Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle); - if (bitmap->isImmutable()) { - ALOGE("Attempting to modify an immutable Bitmap!"); + if (!bitmap->isImmutable()) { + bitmap->notifyPixelsChanged(); } - return bitmap->notifyPixelsChanged(); } namespace { diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp index afd82aca07c5..43cc4f244f71 100644 --- a/libs/hwui/hwui/ImageDecoder.cpp +++ b/libs/hwui/hwui/ImageDecoder.cpp @@ -50,10 +50,8 @@ ImageDecoder::ImageDecoder(std::unique_ptr<SkAndroidCodec> codec, sk_sp<SkPngChu } SkAlphaType ImageDecoder::getOutAlphaType() const { - // While an SkBitmap may want to use kOpaque_SkAlphaType for a performance - // optimization, this class just outputs raw pixels. Using either - // premultiplication choice has no effect on decoding an opaque encoded image. - return mUnpremultipliedRequired ? kUnpremul_SkAlphaType : kPremul_SkAlphaType; + return opaque() ? kOpaque_SkAlphaType + : mUnpremultipliedRequired ? kUnpremul_SkAlphaType : kPremul_SkAlphaType; } bool ImageDecoder::setTargetSize(int width, int height) { @@ -82,8 +80,7 @@ bool ImageDecoder::setTargetSize(int width, int height) { SkISize targetSize = { width, height }, decodeSize = targetSize; int sampleSize = mCodec->computeSampleSize(&decodeSize); - if (decodeSize != targetSize && mUnpremultipliedRequired - && !mCodec->getInfo().isOpaque()) { + if (decodeSize != targetSize && mUnpremultipliedRequired && !opaque()) { return false; } diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp index 055075d0c42a..1ff156593c41 100644 --- a/libs/hwui/jni/AnimatedImageDrawable.cpp +++ b/libs/hwui/jni/AnimatedImageDrawable.cpp @@ -183,7 +183,7 @@ public: } ~InvokeListener() override { - auto* env = get_env_or_die(mJvm); + auto* env = requireEnv(mJvm); env->DeleteWeakGlobalRef(mWeakRef); } diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp index ba669053ed63..c0663a9bc699 100755 --- a/libs/hwui/jni/Bitmap.cpp +++ b/libs/hwui/jni/Bitmap.cpp @@ -601,6 +601,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { android::Parcel* p = parcelForJavaObject(env, parcel); + const bool isMutable = p->readInt32() != 0; const SkColorType colorType = (SkColorType)p->readInt32(); const SkAlphaType alphaType = (SkAlphaType)p->readInt32(); const uint32_t colorSpaceSize = p->readUint32(); @@ -649,7 +650,9 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { // Map the bitmap in place from the ashmem region if possible otherwise copy. sk_sp<Bitmap> nativeBitmap; - if (blob.fd() >= 0 && !blob.isMutable()) { + // If the blob is mutable we have ownership of the region and can always use it + // If the blob is immutable _and_ we're immutable, we can then still use it + if (blob.fd() >= 0 && (blob.isMutable() || !isMutable)) { #if DEBUG_PARCEL ALOGD("Bitmap.createFromParcel: mapped contents of bitmap from %s blob " "(fds %s)", @@ -669,7 +672,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { // Map the pixels in place and take ownership of the ashmem region. We must also respect the // rowBytes value already set on the bitmap instead of attempting to compute our own. nativeBitmap = Bitmap::createFrom(bitmap->info(), bitmap->rowBytes(), dupFd, - const_cast<void*>(blob.data()), size, true); + const_cast<void*>(blob.data()), size, !isMutable); if (!nativeBitmap) { close(dupFd); blob.release(); @@ -707,7 +710,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { } return createBitmap(env, nativeBitmap.release(), - getPremulBitmapCreateFlags(false), NULL, NULL, density); + getPremulBitmapCreateFlags(isMutable), NULL, NULL, density); #else doThrowRE(env, "Cannot use parcels outside of Android"); return NULL; @@ -728,6 +731,7 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, auto bitmapWrapper = reinterpret_cast<BitmapWrapper*>(bitmapHandle); bitmapWrapper->getSkBitmap(&bitmap); + p->writeInt32(!bitmap.isImmutable()); p->writeInt32(bitmap.colorType()); p->writeInt32(bitmap.alphaType()); SkColorSpace* colorSpace = bitmap.colorSpace(); @@ -754,7 +758,7 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, // Transfer the underlying ashmem region if we have one and it's immutable. android::status_t status; int fd = bitmapWrapper->bitmap().getAshmemFd(); - if (fd >= 0 && p->allowFds()) { + if (fd >= 0 && bitmap.isImmutable() && p->allowFds()) { #if DEBUG_PARCEL ALOGD("Bitmap.writeToParcel: transferring immutable bitmap's ashmem fd as " "immutable blob (fds %s)", @@ -775,9 +779,10 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, p->allowFds() ? "allowed" : "forbidden"); #endif + const bool mutableCopy = !bitmap.isImmutable(); size_t size = bitmap.computeByteSize(); android::Parcel::WritableBlob blob; - status = p->writeBlob(size, false, &blob); + status = p->writeBlob(size, mutableCopy, &blob); if (status) { doThrowRE(env, "Could not copy bitmap to parcel blob."); return JNI_FALSE; diff --git a/libs/hwui/jni/ByteBufferStreamAdaptor.cpp b/libs/hwui/jni/ByteBufferStreamAdaptor.cpp index db5f6f6c684f..b10540cb3fbd 100644 --- a/libs/hwui/jni/ByteBufferStreamAdaptor.cpp +++ b/libs/hwui/jni/ByteBufferStreamAdaptor.cpp @@ -9,24 +9,6 @@ using namespace android; static jmethodID gByteBuffer_getMethodID; static jmethodID gByteBuffer_setPositionMethodID; -/** - * Helper method for accessing the JNI interface pointer. - * - * Image decoding (which this supports) is started on a thread that is already - * attached to the Java VM. But an AnimatedImageDrawable continues decoding on - * the AnimatedImageThread, which is not attached. This will attach if - * necessary. - */ -static JNIEnv* requireEnv(JavaVM* jvm) { - JNIEnv* env; - if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { - if (jvm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) { - LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!"); - } - } - return env; -} - class ByteBufferStream : public SkStreamAsset { private: ByteBufferStream(JavaVM* jvm, jobject jbyteBuffer, size_t initialPosition, size_t length, @@ -304,7 +286,7 @@ std::unique_ptr<SkStream> CreateByteBufferStreamAdaptor(JNIEnv* env, jobject jby auto* context = new release_proc_context{jvm, jbyteBuffer}; auto releaseProc = [](const void*, void* context) { auto* c = reinterpret_cast<release_proc_context*>(context); - JNIEnv* env = get_env_or_die(c->jvm); + JNIEnv* env = requireEnv(c->jvm); env->DeleteGlobalRef(c->jbyteBuffer); delete c; }; diff --git a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp index 39483b55992b..f1c6b29204b2 100644 --- a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp +++ b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp @@ -49,13 +49,13 @@ public: } ~JavaInputStreamAdaptor() override { - auto* env = android::get_env_or_die(fJvm); + auto* env = android::requireEnv(fJvm); env->DeleteGlobalRef(fJavaInputStream); env->DeleteGlobalRef(fJavaByteArray); } size_t read(void* buffer, size_t size) override { - auto* env = android::get_env_or_die(fJvm); + auto* env = android::requireEnv(fJvm); if (!fSwallowExceptions && checkException(env)) { // Just in case the caller did not clear from a previous exception. return 0; diff --git a/libs/hwui/jni/GraphicsStatsService.cpp b/libs/hwui/jni/GraphicsStatsService.cpp index 6076552fc094..1591ffabd26a 100644 --- a/libs/hwui/jni/GraphicsStatsService.cpp +++ b/libs/hwui/jni/GraphicsStatsService.cpp @@ -158,17 +158,17 @@ static AStatsManager_PullAtomCallbackReturn graphicsStatsPullCallback(int32_t at static void nativeInit(JNIEnv* env, jobject javaObject) { gGraphicsStatsServiceObject = env->NewGlobalRef(javaObject); AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain(); - AStatsManager_PullAtomMetadata_setCoolDownNs(metadata, 10 * 1000000); // 10 milliseconds - AStatsManager_PullAtomMetadata_setTimeoutNs(metadata, 2 * NS_PER_SEC); // 2 seconds + AStatsManager_PullAtomMetadata_setCoolDownMillis(metadata, 10); // 10 milliseconds + AStatsManager_PullAtomMetadata_setTimeoutMillis(metadata, 2 * MS_PER_SEC); // 2 seconds - AStatsManager_registerPullAtomCallback(android::util::GRAPHICS_STATS, - &graphicsStatsPullCallback, metadata, nullptr); + AStatsManager_setPullAtomCallback(android::util::GRAPHICS_STATS, metadata, + &graphicsStatsPullCallback, nullptr); AStatsManager_PullAtomMetadata_release(metadata); } static void nativeDestructor(JNIEnv* env, jobject javaObject) { - AStatsManager_unregisterPullAtomCallback(android::util::GRAPHICS_STATS); + AStatsManager_clearPullAtomCallback(android::util::GRAPHICS_STATS); env->DeleteGlobalRef(gGraphicsStatsServiceObject); gGraphicsStatsServiceObject = nullptr; } diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp index b6b378539bd0..41d939bd6373 100644 --- a/libs/hwui/jni/ImageDecoder.cpp +++ b/libs/hwui/jni/ImageDecoder.cpp @@ -305,9 +305,6 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong } SkImageInfo bitmapInfo = decoder->getOutputInfo(); - if (decoder->opaque()) { - bitmapInfo = bitmapInfo.makeAlphaType(kOpaque_SkAlphaType); - } if (asAlphaMask && colorType == kGray_8_SkColorType) { bitmapInfo = bitmapInfo.makeColorType(kAlpha_8_SkColorType); } diff --git a/libs/hwui/jni/Utils.cpp b/libs/hwui/jni/Utils.cpp index 17c194d04f84..34fd6687d52c 100644 --- a/libs/hwui/jni/Utils.cpp +++ b/libs/hwui/jni/Utils.cpp @@ -159,3 +159,13 @@ JNIEnv* android::get_env_or_die(JavaVM* jvm) { } return env; } + +JNIEnv* android::requireEnv(JavaVM* jvm) { + JNIEnv* env; + if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + if (jvm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!"); + } + } + return env; +} diff --git a/libs/hwui/jni/Utils.h b/libs/hwui/jni/Utils.h index 89255177ba2e..f628cc3c85ed 100644 --- a/libs/hwui/jni/Utils.h +++ b/libs/hwui/jni/Utils.h @@ -78,6 +78,16 @@ bool isSeekable(int descriptor); JNIEnv* get_env_or_die(JavaVM* jvm); +/** + * Helper method for accessing the JNI interface pointer. + * + * Image decoding (which this supports) is started on a thread that is already + * attached to the Java VM. But an AnimatedImageDrawable continues decoding on + * the AnimatedImageThread, which is not attached. This will attach if + * necessary. + */ +JNIEnv* requireEnv(JavaVM* jvm); + }; // namespace android #endif // _ANDROID_GRAPHICS_UTILS_H_ diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 41aa1ff80e3c..5088494d6a07 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -418,9 +418,9 @@ void SkiaPipeline::endCapture(SkSurface* surface) { auto data = picture->serialize(); savePictureAsync(data, mCapturedFile); mCaptureSequence = 0; + mCaptureMode = CaptureMode::None; } } - mCaptureMode = CaptureMode::None; mRecorder.reset(); } } diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 4299dd3b46fe..335bcdcfc1fb 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -440,6 +440,12 @@ void CanvasContext::draw() { if (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw()) { mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); + // Notify the callbacks, even if there's nothing to draw so they aren't waiting + // indefinitely + for (auto& func : mFrameCompleteCallbacks) { + std::invoke(func, mFrameNumber); + } + mFrameCompleteCallbacks.clear(); return; } @@ -555,9 +561,11 @@ void CanvasContext::draw() { FrameInfo* forthBehind = mLast4FrameInfos.front().first; int64_t composedFrameId = mLast4FrameInfos.front().second; nsecs_t acquireTime = -1; - native_window_get_frame_timestamps(mNativeSurface->getNativeWindow(), composedFrameId, - nullptr, &acquireTime, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr); + if (mNativeSurface) { + native_window_get_frame_timestamps(mNativeSurface->getNativeWindow(), composedFrameId, + nullptr, &acquireTime, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr); + } // Ignore default -1, NATIVE_WINDOW_TIMESTAMP_INVALID and NATIVE_WINDOW_TIMESTAMP_PENDING forthBehind->set(FrameInfoIndex::GpuCompleted) = acquireTime > 0 ? acquireTime : -1; mJankTracker.finishGpuDraw(*forthBehind); diff --git a/libs/hwui/tests/unit/ABitmapTests.cpp b/libs/hwui/tests/unit/ABitmapTests.cpp new file mode 100644 index 000000000000..8e2f7e09d406 --- /dev/null +++ b/libs/hwui/tests/unit/ABitmapTests.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include "android/graphics/bitmap.h" +#include "apex/TypeCast.h" +#include "hwui/Bitmap.h" +#include "tests/common/TestUtils.h" + +using namespace android; +using namespace android::uirenderer; + +TEST(ABitmap, notifyPixelsChanged) { + // generate a bitmap and its public API handle + sk_sp<Bitmap> bitmap(TestUtils::createBitmap(1, 1)); + ABitmap* abmp = android::TypeCast::toABitmap(bitmap.get()); + + // verify that notification changes the genID + uint32_t genID = bitmap->getGenerationID(); + ABitmap_notifyPixelsChanged(abmp); + ASSERT_TRUE(bitmap->getGenerationID() != genID); + + // mark the bitmap as immutable + ASSERT_FALSE(bitmap->isImmutable()); + bitmap->setImmutable(); + ASSERT_TRUE(bitmap->isImmutable()); + + // attempt to notify that the pixels have changed + genID = bitmap->getGenerationID(); + ABitmap_notifyPixelsChanged(abmp); + ASSERT_TRUE(bitmap->getGenerationID() == genID); +} diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index 90bcd1c0e370..1208062d9da0 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -403,3 +403,40 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) { renderThread.destroyRenderingContext(); EXPECT_FALSE(pipeline->isSurfaceReady()); } + +RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, pictureCallback) { + // create a pipeline and add a picture callback + auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); + int callbackCount = 0; + pipeline->setPictureCapturedCallback( + [&callbackCount](sk_sp<SkPicture>&& picture) { callbackCount += 1; }); + + // create basic red frame and render it + auto redNode = TestUtils::createSkiaNode( + 0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) { + redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); + }); + LayerUpdateQueue layerUpdateQueue; + SkRect dirty = SkRectMakeLargest(); + std::vector<sp<RenderNode>> renderNodes; + renderNodes.push_back(redNode); + bool opaque = true; + android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1); + auto surface = SkSurface::MakeRasterN32Premul(1, 1); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, + SkMatrix::I()); + + // verify the callback was called + EXPECT_EQ(1, callbackCount); + + // render a second frame and check the callback count + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, + SkMatrix::I()); + EXPECT_EQ(2, callbackCount); + + // unset the callback, render another frame, check callback was not invoked + pipeline->setPictureCapturedCallback(nullptr); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, + SkMatrix::I()); + EXPECT_EQ(2, callbackCount); +} |