| /* | |
| * Copyright (C) 2010 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. | |
| */ | |
| #define ATRACE_TAG ATRACE_TAG_VIEW | |
| #include <FrameInfo.h> | |
| #include <GraphicsJNI.h> | |
| #include <Picture.h> | |
| #include <Properties.h> | |
| #include <RootRenderNode.h> | |
| #include <SkBitmap.h> | |
| #include <SkColorSpace.h> | |
| #include <SkData.h> | |
| #include <SkImage.h> | |
| #include <SkImageAndroid.h> | |
| #include <SkPicture.h> | |
| #include <SkPixmap.h> | |
| #include <SkSerialProcs.h> | |
| #include <SkStream.h> | |
| #include <SkTypeface.h> | |
| #include <dlfcn.h> | |
| #include <gui/TraceUtils.h> | |
| #include <include/encode/SkPngEncoder.h> | |
| #include <inttypes.h> | |
| #include <log/log.h> | |
| #include <media/NdkImage.h> | |
| #include <media/NdkImageReader.h> | |
| #include <nativehelper/JNIPlatformHelp.h> | |
| #include <pipeline/skia/ShaderCache.h> | |
| #include <private/EGL/cache.h> | |
| #include <renderthread/CanvasContext.h> | |
| #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> | |
| #include <utils/StrongPointer.h> | |
| #include <utils/Timers.h> | |
| #include <algorithm> | |
| #include <atomic> | |
| #include <vector> | |
| #include "JvmErrorReporter.h" | |
| #include "android_graphics_HardwareRendererObserver.h" | |
| #include "utils/ForceDark.h" | |
| namespace android { | |
| using namespace android::uirenderer; | |
| using namespace android::uirenderer::renderthread; | |
| struct { | |
| jclass clazz; | |
| jmethodID invokePictureCapturedCallback; | |
| } gHardwareRenderer; | |
| struct { | |
| jmethodID onMergeTransaction; | |
| } gASurfaceTransactionCallback; | |
| struct { | |
| jmethodID prepare; | |
| } gPrepareSurfaceControlForWebviewCallback; | |
| struct { | |
| jmethodID onFrameDraw; | |
| } gFrameDrawingCallback; | |
| struct { | |
| jmethodID onFrameCommit; | |
| } gFrameCommitCallback; | |
| struct { | |
| jmethodID onFrameComplete; | |
| } gFrameCompleteCallback; | |
| struct { | |
| jmethodID onCopyFinished; | |
| jmethodID getDestinationBitmap; | |
| } gCopyRequest; | |
| static JNIEnv* getenv(JavaVM* vm) { | |
| JNIEnv* env; | |
| if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { | |
| LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm); | |
| } | |
| return env; | |
| } | |
| typedef ANativeWindow* (*ANW_fromSurface)(JNIEnv* env, jobject surface); | |
| ANW_fromSurface fromSurface; | |
| class FrameCommitWrapper : public LightRefBase<FrameCommitWrapper> { | |
| public: | |
| explicit FrameCommitWrapper(JNIEnv* env, jobject jobject) { | |
| env->GetJavaVM(&mVm); | |
| mObject = env->NewGlobalRef(jobject); | |
| LOG_ALWAYS_FATAL_IF(!mObject, "Failed to make global ref"); | |
| } | |
| ~FrameCommitWrapper() { releaseObject(); } | |
| void onFrameCommit(bool didProduceBuffer) { | |
| if (mObject) { | |
| ATRACE_FORMAT("frameCommit success=%d", didProduceBuffer); | |
| getenv(mVm)->CallVoidMethod(mObject, gFrameCommitCallback.onFrameCommit, | |
| didProduceBuffer); | |
| releaseObject(); | |
| } | |
| } | |
| private: | |
| JavaVM* mVm; | |
| jobject mObject; | |
| void releaseObject() { | |
| if (mObject) { | |
| getenv(mVm)->DeleteGlobalRef(mObject); | |
| mObject = nullptr; | |
| } | |
| } | |
| }; | |
| static void android_view_ThreadedRenderer_rotateProcessStatsBuffer(JNIEnv* env, jobject clazz) { | |
| RenderProxy::rotateProcessStatsBuffer(); | |
| } | |
| static void android_view_ThreadedRenderer_setProcessStatsBuffer(JNIEnv* env, jobject clazz, | |
| jint fd) { | |
| RenderProxy::setProcessStatsBuffer(fd); | |
| } | |
| static jint android_view_ThreadedRenderer_getRenderThreadTid(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| return proxy->getRenderThreadTid(); | |
| } | |
| static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) { | |
| RootRenderNode* node = new RootRenderNode(std::make_unique<JvmErrorReporter>(env)); | |
| node->incStrong(0); | |
| node->setName("RootRenderNode"); | |
| return reinterpret_cast<jlong>(node); | |
| } | |
| static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz, | |
| jboolean translucent, jlong rootRenderNodePtr) { | |
| RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootRenderNodePtr); | |
| ContextFactoryImpl factory(rootRenderNode); | |
| RenderProxy* proxy = new RenderProxy(translucent, rootRenderNode, &factory); | |
| return (jlong) proxy; | |
| } | |
| static void android_view_ThreadedRenderer_deleteProxy(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| delete proxy; | |
| } | |
| static jboolean android_view_ThreadedRenderer_loadSystemProperties(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| return proxy->loadSystemProperties(); | |
| } | |
| static void android_view_ThreadedRenderer_setName(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr, jstring jname) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| const char* name = env->GetStringUTFChars(jname, NULL); | |
| proxy->setName(name); | |
| env->ReleaseStringUTFChars(jname, name); | |
| } | |
| static void android_view_ThreadedRenderer_setSurface(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr, jobject jsurface, jboolean discardBuffer) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| ANativeWindow* window = nullptr; | |
| if (jsurface) { | |
| window = fromSurface(env, jsurface); | |
| } | |
| bool enableTimeout = true; | |
| if (discardBuffer) { | |
| // Currently only Surface#lockHardwareCanvas takes this path | |
| enableTimeout = false; | |
| proxy->setSwapBehavior(SwapBehavior::kSwap_discardBuffer); | |
| } | |
| proxy->setSurface(window, enableTimeout); | |
| if (window) { | |
| ANativeWindow_release(window); | |
| } | |
| } | |
| static void android_view_ThreadedRenderer_setSurfaceControl(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr, jlong surfaceControlPtr) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| ASurfaceControl* surfaceControl = reinterpret_cast<ASurfaceControl*>(surfaceControlPtr); | |
| proxy->setSurfaceControl(surfaceControl); | |
| } | |
| static jboolean android_view_ThreadedRenderer_pause(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| return proxy->pause(); | |
| } | |
| static void android_view_ThreadedRenderer_setStopped(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr, jboolean stopped) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| proxy->setStopped(stopped); | |
| } | |
| static void android_view_ThreadedRenderer_setLightAlpha(JNIEnv* env, jobject clazz, jlong proxyPtr, | |
| jfloat ambientShadowAlpha, jfloat spotShadowAlpha) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| proxy->setLightAlpha((uint8_t) (255 * ambientShadowAlpha), (uint8_t) (255 * spotShadowAlpha)); | |
| } | |
| static void android_view_ThreadedRenderer_setLightGeometry(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr, jfloat lightX, jfloat lightY, jfloat lightZ, jfloat lightRadius) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| proxy->setLightGeometry((Vector3){lightX, lightY, lightZ}, lightRadius); | |
| } | |
| static void android_view_ThreadedRenderer_setOpaque(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr, jboolean opaque) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| proxy->setOpaque(opaque); | |
| } | |
| static jfloat android_view_ThreadedRenderer_setColorMode(JNIEnv* env, jobject clazz, jlong proxyPtr, | |
| jint colorMode) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| return proxy->setColorMode(static_cast<ColorMode>(colorMode)); | |
| } | |
| static void android_view_ThreadedRenderer_setTargetSdrHdrRatio(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr, jfloat ratio) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| return proxy->setRenderSdrHdrRatio(ratio); | |
| } | |
| static void android_view_ThreadedRenderer_setSdrWhitePoint(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr, jfloat sdrWhitePoint) { | |
| Properties::defaultSdrWhitePoint = sdrWhitePoint; | |
| } | |
| static void android_view_ThreadedRenderer_setIsHighEndGfx(JNIEnv* env, jobject clazz, | |
| jboolean jIsHighEndGfx) { | |
| Properties::setIsHighEndGfx(jIsHighEndGfx); | |
| } | |
| static void android_view_ThreadedRenderer_setIsLowRam(JNIEnv* env, jobject clazz, | |
| jboolean isLowRam) { | |
| Properties::setIsLowRam(isLowRam); | |
| } | |
| static void android_view_ThreadedRenderer_setIsSystemOrPersistent(JNIEnv* env, jobject clazz, | |
| jboolean isSystemOrPersistent) { | |
| Properties::setIsSystemOrPersistent(isSystemOrPersistent); | |
| } | |
| static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr, jlongArray frameInfo, | |
| jint frameInfoSize) { | |
| LOG_ALWAYS_FATAL_IF(frameInfoSize != UI_THREAD_FRAME_INFO_SIZE, | |
| "Mismatched size expectations, given %d expected %zu", frameInfoSize, | |
| UI_THREAD_FRAME_INFO_SIZE); | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| env->GetLongArrayRegion(frameInfo, 0, frameInfoSize, proxy->frameInfo()); | |
| return proxy->syncAndDrawFrame(); | |
| } | |
| static void android_view_ThreadedRenderer_destroy(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr, jlong rootNodePtr) { | |
| RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr); | |
| rootRenderNode->destroy(); | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| proxy->destroy(); | |
| } | |
| static void android_view_ThreadedRenderer_registerAnimatingRenderNode(JNIEnv* env, jobject clazz, | |
| jlong rootNodePtr, jlong animatingNodePtr) { | |
| RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr); | |
| RenderNode* animatingNode = reinterpret_cast<RenderNode*>(animatingNodePtr); | |
| rootRenderNode->attachAnimatingNode(animatingNode); | |
| } | |
| static void android_view_ThreadedRenderer_registerVectorDrawableAnimator(JNIEnv* env, jobject clazz, | |
| jlong rootNodePtr, jlong animatorPtr) { | |
| RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr); | |
| PropertyValuesAnimatorSet* animator = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorPtr); | |
| rootRenderNode->addVectorDrawableAnimator(animator); | |
| } | |
| static jlong android_view_ThreadedRenderer_createTextureLayer(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| DeferredLayerUpdater* layer = proxy->createTextureLayer(); | |
| return reinterpret_cast<jlong>(layer); | |
| } | |
| static void android_view_ThreadedRenderer_buildLayer(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr, jlong nodePtr) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| RenderNode* node = reinterpret_cast<RenderNode*>(nodePtr); | |
| proxy->buildLayer(node); | |
| } | |
| static jboolean android_view_ThreadedRenderer_copyLayerInto(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr, jlong layerPtr, jlong bitmapPtr) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr); | |
| SkBitmap bitmap; | |
| bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); | |
| return proxy->copyLayerInto(layer, bitmap); | |
| } | |
| static void android_view_ThreadedRenderer_pushLayerUpdate(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr, jlong layerPtr) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr); | |
| proxy->pushLayerUpdate(layer); | |
| } | |
| static void android_view_ThreadedRenderer_cancelLayerUpdate(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr, jlong layerPtr) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr); | |
| proxy->cancelLayerUpdate(layer); | |
| } | |
| static void android_view_ThreadedRenderer_detachSurfaceTexture(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr, jlong layerPtr) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr); | |
| proxy->detachSurfaceTexture(layer); | |
| } | |
| static void android_view_ThreadedRenderer_destroyHardwareResources(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| proxy->destroyHardwareResources(); | |
| } | |
| static void android_view_ThreadedRenderer_trimMemory(JNIEnv* env, jobject clazz, | |
| jint level) { | |
| RenderProxy::trimMemory(level); | |
| } | |
| static void android_view_ThreadedRenderer_trimCaches(JNIEnv* env, jobject clazz, jint level) { | |
| RenderProxy::trimCaches(level); | |
| } | |
| static void android_view_ThreadedRenderer_overrideProperty(JNIEnv* env, jobject clazz, | |
| jstring name, jstring value) { | |
| const char* nameCharArray = env->GetStringUTFChars(name, NULL); | |
| const char* valueCharArray = env->GetStringUTFChars(value, NULL); | |
| RenderProxy::overrideProperty(nameCharArray, valueCharArray); | |
| env->ReleaseStringUTFChars(name, nameCharArray); | |
| env->ReleaseStringUTFChars(name, valueCharArray); | |
| } | |
| static void android_view_ThreadedRenderer_fence(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| proxy->fence(); | |
| } | |
| static void android_view_ThreadedRenderer_stopDrawing(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| proxy->stopDrawing(); | |
| } | |
| static void android_view_ThreadedRenderer_notifyFramePending(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| proxy->notifyFramePending(); | |
| } | |
| static void android_view_ThreadedRenderer_dumpProfileInfo(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr, jobject javaFileDescriptor, jint dumpFlags) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor); | |
| proxy->dumpProfileInfo(fd, dumpFlags); | |
| } | |
| static void android_view_ThreadedRenderer_dumpGlobalProfileInfo(JNIEnv* env, jobject clazz, | |
| jobject javaFileDescriptor, | |
| jint dumpFlags) { | |
| int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor); | |
| RenderProxy::dumpGraphicsMemory(fd, true, dumpFlags & DumpFlags::Reset); | |
| } | |
| static void android_view_ThreadedRenderer_addRenderNode(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr, jlong renderNodePtr, jboolean placeFront) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); | |
| proxy->addRenderNode(renderNode, placeFront); | |
| } | |
| static void android_view_ThreadedRenderer_removeRenderNode(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr, jlong renderNodePtr) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); | |
| proxy->removeRenderNode(renderNode); | |
| } | |
| static void android_view_ThreadedRendererd_drawRenderNode(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr, jlong renderNodePtr) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); | |
| proxy->drawRenderNode(renderNode); | |
| } | |
| static void android_view_ThreadedRenderer_setContentDrawBounds(JNIEnv* env, | |
| jobject clazz, jlong proxyPtr, jint left, jint top, jint right, jint bottom) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| proxy->setContentDrawBounds(left, top, right, bottom); | |
| } | |
| static void android_view_ThreadedRenderer_forceDrawNextFrame(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| proxy->forceDrawNextFrame(); | |
| } | |
| 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 serializable-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 SkImages::RasterFromBitmap(*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 SkImages::PinnableRasterFromBitmap(bm); | |
| } | |
| 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(); | |
| } | |
| if (!img) { | |
| return nullptr; | |
| } | |
| // The following encode (specifically the pixel readback) will fail on a | |
| // texture-backed image. They should already be raster images, but on | |
| // the off-chance they aren't, we will just serialize it as nothing. | |
| if (img->isTextureBacked()) { | |
| return SkData::MakeEmpty(); | |
| } | |
| return SkPngEncoder::Encode(nullptr, img, {}); | |
| } | |
| 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); | |
| if (!pictureCallback) { | |
| proxy->setPictureCapturedCallback(nullptr); | |
| } else { | |
| JavaVM* vm = nullptr; | |
| LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM"); | |
| auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(vm, | |
| env->NewGlobalRef(pictureCallback)); | |
| auto pictureState = std::make_shared<PictureCaptureState>(); | |
| proxy->setPictureCapturedCallback([globalCallbackRef, | |
| pictureState](sk_sp<SkPicture>&& picture) { | |
| Picture* wrapper = new PictureWrapper{std::move(picture), pictureState}; | |
| globalCallbackRef->env()->CallStaticVoidMethod( | |
| gHardwareRenderer.clazz, gHardwareRenderer.invokePictureCapturedCallback, | |
| static_cast<jlong>(reinterpret_cast<intptr_t>(wrapper)), | |
| globalCallbackRef->object()); | |
| }); | |
| } | |
| } | |
| static void android_view_ThreadedRenderer_setASurfaceTransactionCallback( | |
| JNIEnv* env, jobject clazz, jlong proxyPtr, jobject aSurfaceTransactionCallback) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| if (!aSurfaceTransactionCallback) { | |
| proxy->setASurfaceTransactionCallback(nullptr); | |
| } else { | |
| JavaVM* vm = nullptr; | |
| LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM"); | |
| auto globalCallbackRef = std::make_shared<JGlobalRefHolder>( | |
| vm, env->NewGlobalRef(aSurfaceTransactionCallback)); | |
| proxy->setASurfaceTransactionCallback([globalCallbackRef](int64_t transObj, int64_t scObj, | |
| int64_t frameNr) -> bool { | |
| jboolean ret = globalCallbackRef->env()->CallBooleanMethod( | |
| globalCallbackRef->object(), gASurfaceTransactionCallback.onMergeTransaction, | |
| static_cast<jlong>(transObj), static_cast<jlong>(scObj), | |
| static_cast<jlong>(frameNr)); | |
| return ret; | |
| }); | |
| } | |
| } | |
| static void android_view_ThreadedRenderer_setPrepareSurfaceControlForWebviewCallback( | |
| JNIEnv* env, jobject clazz, jlong proxyPtr, jobject callback) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| if (!callback) { | |
| proxy->setPrepareSurfaceControlForWebviewCallback(nullptr); | |
| } else { | |
| JavaVM* vm = nullptr; | |
| LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM"); | |
| auto globalCallbackRef = | |
| std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(callback)); | |
| proxy->setPrepareSurfaceControlForWebviewCallback([globalCallbackRef]() { | |
| globalCallbackRef->env()->CallVoidMethod( | |
| globalCallbackRef->object(), gPrepareSurfaceControlForWebviewCallback.prepare); | |
| }); | |
| } | |
| } | |
| static void android_view_ThreadedRenderer_setFrameCallback(JNIEnv* env, | |
| jobject clazz, jlong proxyPtr, jobject frameCallback) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| if (!frameCallback) { | |
| proxy->setFrameCallback(nullptr); | |
| } else { | |
| JavaVM* vm = nullptr; | |
| LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM"); | |
| auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(vm, | |
| env->NewGlobalRef(frameCallback)); | |
| proxy->setFrameCallback([globalCallbackRef](int32_t syncResult, | |
| int64_t frameNr) -> std::function<void(bool)> { | |
| JNIEnv* env = globalCallbackRef->env(); | |
| ScopedLocalRef<jobject> frameCommitCallback( | |
| env, env->CallObjectMethod( | |
| globalCallbackRef->object(), gFrameDrawingCallback.onFrameDraw, | |
| static_cast<jint>(syncResult), static_cast<jlong>(frameNr))); | |
| if (frameCommitCallback == nullptr) { | |
| return nullptr; | |
| } | |
| sp<FrameCommitWrapper> wrapper = | |
| sp<FrameCommitWrapper>::make(env, frameCommitCallback.get()); | |
| return [wrapper](bool didProduceBuffer) { wrapper->onFrameCommit(didProduceBuffer); }; | |
| }); | |
| } | |
| } | |
| static void android_view_ThreadedRenderer_setFrameCommitCallback(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr, jobject callback) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| if (!callback) { | |
| proxy->setFrameCommitCallback(nullptr); | |
| } else { | |
| sp<FrameCommitWrapper> wrapper = sp<FrameCommitWrapper>::make(env, callback); | |
| proxy->setFrameCommitCallback( | |
| [wrapper](bool didProduceBuffer) { wrapper->onFrameCommit(didProduceBuffer); }); | |
| } | |
| } | |
| static void android_view_ThreadedRenderer_setFrameCompleteCallback(JNIEnv* env, | |
| jobject clazz, jlong proxyPtr, jobject callback) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| if (!callback) { | |
| proxy->setFrameCompleteCallback(nullptr); | |
| } else { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| JavaVM* vm = nullptr; | |
| LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM"); | |
| auto globalCallbackRef = | |
| std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(callback)); | |
| proxy->setFrameCompleteCallback([globalCallbackRef]() { | |
| globalCallbackRef->env()->CallVoidMethod(globalCallbackRef->object(), | |
| gFrameCompleteCallback.onFrameComplete); | |
| }); | |
| } | |
| } | |
| class CopyRequestAdapter : public CopyRequest { | |
| public: | |
| CopyRequestAdapter(JavaVM* vm, jobject jCopyRequest, Rect srcRect) | |
| : CopyRequest(srcRect), mRefHolder(vm, jCopyRequest) {} | |
| virtual SkBitmap getDestinationBitmap(int srcWidth, int srcHeight) override { | |
| jlong bitmapPtr = mRefHolder.env()->CallLongMethod( | |
| mRefHolder.object(), gCopyRequest.getDestinationBitmap, srcWidth, srcHeight); | |
| SkBitmap bitmap; | |
| bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); | |
| return bitmap; | |
| } | |
| virtual void onCopyFinished(CopyResult result) override { | |
| mRefHolder.env()->CallVoidMethod(mRefHolder.object(), gCopyRequest.onCopyFinished, | |
| static_cast<jint>(result)); | |
| } | |
| private: | |
| JGlobalRefHolder mRefHolder; | |
| }; | |
| static void android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env, jobject clazz, | |
| jobject jsurface, jint left, jint top, | |
| jint right, jint bottom, | |
| jobject jCopyRequest) { | |
| JavaVM* vm = nullptr; | |
| LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM"); | |
| auto copyRequest = std::make_shared<CopyRequestAdapter>(vm, env->NewGlobalRef(jCopyRequest), | |
| Rect(left, top, right, bottom)); | |
| ANativeWindow* window = fromSurface(env, jsurface); | |
| RenderProxy::copySurfaceInto(window, std::move(copyRequest)); | |
| ANativeWindow_release(window); | |
| } | |
| class ContextFactory : public IContextFactory { | |
| public: | |
| virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) { | |
| return new AnimationContext(clock); | |
| } | |
| }; | |
| static jobject android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode(JNIEnv* env, | |
| jobject clazz, jlong renderNodePtr, jint jwidth, jint jheight) { | |
| RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); | |
| if (jwidth <= 0 || jheight <= 0) { | |
| ALOGW("Invalid width %d or height %d", jwidth, jheight); | |
| return nullptr; | |
| } | |
| uint32_t width = jwidth; | |
| uint32_t height = jheight; | |
| // Create an ImageReader wired up to a BufferItemConsumer | |
| AImageReader* rawReader; | |
| constexpr auto usage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | | |
| AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER | | |
| AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY; | |
| media_status_t result = | |
| AImageReader_newWithUsage(width, height, AIMAGE_FORMAT_RGBA_8888, usage, 2, &rawReader); | |
| std::unique_ptr<AImageReader, decltype(&AImageReader_delete)> reader(rawReader, | |
| AImageReader_delete); | |
| if (result != AMEDIA_OK) { | |
| ALOGW("Error creating image reader!"); | |
| return nullptr; | |
| } | |
| // Note that ownership of this window is maintained by AImageReader, so we | |
| // shouldn't need to wrap around a smart pointer. | |
| ANativeWindow* window; | |
| result = AImageReader_getWindow(rawReader, &window); | |
| if (result != AMEDIA_OK) { | |
| ALOGW("Error retrieving the native window!"); | |
| return nullptr; | |
| } | |
| // Render into the surface | |
| { | |
| ContextFactory factory; | |
| RenderProxy proxy{true, renderNode, &factory}; | |
| proxy.setSwapBehavior(SwapBehavior::kSwap_discardBuffer); | |
| proxy.setSurface(window); | |
| // Shadows can't be used via this interface, so just set the light source | |
| // to all 0s. | |
| proxy.setLightAlpha(0, 0); | |
| proxy.setLightGeometry((Vector3){0, 0, 0}, 0); | |
| nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC); | |
| UiFrameInfoBuilder(proxy.frameInfo()) | |
| .setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID, | |
| UiFrameInfoBuilder::UNKNOWN_DEADLINE, | |
| UiFrameInfoBuilder::UNKNOWN_FRAME_INTERVAL) | |
| .addFlag(FrameInfoFlags::SurfaceCanvas); | |
| proxy.syncAndDrawFrame(); | |
| } | |
| AImage* rawImage; | |
| result = AImageReader_acquireNextImage(rawReader, &rawImage); | |
| std::unique_ptr<AImage, decltype(&AImage_delete)> image(rawImage, AImage_delete); | |
| if (result != AMEDIA_OK) { | |
| ALOGW("Error reading image: %d!", result); | |
| return nullptr; | |
| } | |
| AHardwareBuffer* buffer; | |
| result = AImage_getHardwareBuffer(rawImage, &buffer); | |
| AHardwareBuffer_Desc desc; | |
| AHardwareBuffer_describe(buffer, &desc); | |
| if (desc.width != width || desc.height != height) { | |
| ALOGW("AHardwareBuffer size mismatch, got %dx%d expected %dx%d", desc.width, desc.height, | |
| width, height); | |
| // Continue I guess? | |
| } | |
| sk_sp<SkColorSpace> cs = uirenderer::DataSpaceToColorSpace( | |
| static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(window))); | |
| if (cs == nullptr) { | |
| // nullptr is treated as SRGB in Skia, thus explicitly use SRGB in order to make sure | |
| // the returned bitmap has a color space. | |
| cs = SkColorSpace::MakeSRGB(); | |
| } | |
| sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, cs); | |
| return bitmap::createBitmap(env, bitmap.release(), | |
| android::bitmap::kBitmapCreateFlag_Premultiplied); | |
| } | |
| static void android_view_ThreadedRenderer_disableVsync(JNIEnv*, jclass) { | |
| RenderProxy::disableVsync(); | |
| } | |
| static void android_view_ThreadedRenderer_setHighContrastText(JNIEnv*, jclass, jboolean enable) { | |
| Properties::enableHighContrastText = enable; | |
| } | |
| static void android_view_ThreadedRenderer_setDebuggingEnabled(JNIEnv*, jclass, jboolean enable) { | |
| Properties::debuggingEnabled = enable; | |
| } | |
| static void android_view_ThreadedRenderer_setIsolatedProcess(JNIEnv*, jclass, jboolean isolated) { | |
| Properties::isolatedProcess = isolated; | |
| } | |
| static void android_view_ThreadedRenderer_setContextPriority(JNIEnv*, jclass, | |
| jint contextPriority) { | |
| Properties::contextPriority = contextPriority; | |
| } | |
| static void android_view_ThreadedRenderer_allocateBuffers(JNIEnv* env, jobject clazz, | |
| jlong proxyPtr) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| proxy->allocateBuffers(); | |
| } | |
| static void android_view_ThreadedRenderer_setForceDark(JNIEnv* env, jobject clazz, jlong proxyPtr, | |
| jint type) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| proxy->setForceDark(static_cast<ForceDarkType>(type)); | |
| } | |
| static void android_view_ThreadedRenderer_preload(JNIEnv*, jclass) { | |
| RenderProxy::preload(); | |
| } | |
| static void android_view_ThreadedRenderer_setRtAnimationsEnabled(JNIEnv* env, jobject clazz, | |
| jboolean enabled) { | |
| RenderProxy::setRtAnimationsEnabled(enabled); | |
| } | |
| static void android_view_ThreadedRenderer_notifyCallbackPending(JNIEnv*, jclass, jlong proxyPtr) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| proxy->notifyCallbackPending(); | |
| } | |
| static void android_view_ThreadedRenderer_notifyExpensiveFrame(JNIEnv*, jclass, jlong proxyPtr) { | |
| RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); | |
| proxy->notifyExpensiveFrame(); | |
| } | |
| // Plumbs the display density down to DeviceInfo. | |
| static void android_view_ThreadedRenderer_setDisplayDensityDpi(JNIEnv*, jclass, jint densityDpi) { | |
| // Convert from dpi to density-independent pixels. | |
| const float density = densityDpi / 160.0; | |
| DeviceInfo::setDensity(density); | |
| } | |
| static void android_view_ThreadedRenderer_initDisplayInfo( | |
| JNIEnv* env, jclass, jint physicalWidth, jint physicalHeight, jfloat refreshRate, | |
| jint wideColorDataspace, jlong appVsyncOffsetNanos, jlong presentationDeadlineNanos, | |
| jboolean supportFp16ForHdr, jboolean supportRgba10101010ForHdr, | |
| jboolean supportMixedColorSpaces) { | |
| DeviceInfo::setWidth(physicalWidth); | |
| DeviceInfo::setHeight(physicalHeight); | |
| DeviceInfo::setRefreshRate(refreshRate); | |
| DeviceInfo::setWideColorDataspace(static_cast<ADataSpace>(wideColorDataspace)); | |
| DeviceInfo::setAppVsyncOffsetNanos(appVsyncOffsetNanos); | |
| DeviceInfo::setPresentationDeadlineNanos(presentationDeadlineNanos); | |
| DeviceInfo::setSupportFp16ForHdr(supportFp16ForHdr); | |
| DeviceInfo::setSupportRgba10101010ForHdr(supportRgba10101010ForHdr); | |
| DeviceInfo::setSupportMixedColorSpaces(supportMixedColorSpaces); | |
| } | |
| static void android_view_ThreadedRenderer_setDrawingEnabled(JNIEnv*, jclass, jboolean enabled) { | |
| Properties::setDrawingEnabled(enabled); | |
| } | |
| static jboolean android_view_ThreadedRenderer_isDrawingEnabled(JNIEnv*, jclass) { | |
| return Properties::isDrawingEnabled(); | |
| } | |
| // ---------------------------------------------------------------------------- | |
| // HardwareRendererObserver | |
| // ---------------------------------------------------------------------------- | |
| static void android_view_ThreadedRenderer_addObserver(JNIEnv* env, jclass clazz, | |
| jlong proxyPtr, jlong observerPtr) { | |
| HardwareRendererObserver* observer = reinterpret_cast<HardwareRendererObserver*>(observerPtr); | |
| renderthread::RenderProxy* renderProxy = | |
| reinterpret_cast<renderthread::RenderProxy*>(proxyPtr); | |
| renderProxy->addFrameMetricsObserver(observer); | |
| } | |
| static void android_view_ThreadedRenderer_removeObserver(JNIEnv* env, jclass clazz, | |
| jlong proxyPtr, jlong observerPtr) { | |
| HardwareRendererObserver* observer = reinterpret_cast<HardwareRendererObserver*>(observerPtr); | |
| renderthread::RenderProxy* renderProxy = | |
| reinterpret_cast<renderthread::RenderProxy*>(proxyPtr); | |
| renderProxy->removeFrameMetricsObserver(observer); | |
| } | |
| // ---------------------------------------------------------------------------- | |
| // Shaders | |
| // ---------------------------------------------------------------------------- | |
| static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz, | |
| jstring diskCachePath, jstring skiaDiskCachePath) { | |
| const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL); | |
| android::egl_set_cache_filename(cacheArray); | |
| env->ReleaseStringUTFChars(diskCachePath, cacheArray); | |
| const char* skiaCacheArray = env->GetStringUTFChars(skiaDiskCachePath, NULL); | |
| uirenderer::skiapipeline::ShaderCache::get().setFilename(skiaCacheArray); | |
| env->ReleaseStringUTFChars(skiaDiskCachePath, skiaCacheArray); | |
| } | |
| static jboolean android_view_ThreadedRenderer_isWebViewOverlaysEnabled(JNIEnv* env, jobject clazz) { | |
| // this value is valid only after loadSystemProperties() is called | |
| return Properties::enableWebViewOverlays; | |
| } | |
| // ---------------------------------------------------------------------------- | |
| // JNI Glue | |
| // ---------------------------------------------------------------------------- | |
| const char* const kClassPathName = "android/graphics/HardwareRenderer"; | |
| static const JNINativeMethod gMethods[] = { | |
| {"nRotateProcessStatsBuffer", "()V", | |
| (void*)android_view_ThreadedRenderer_rotateProcessStatsBuffer}, | |
| {"nSetProcessStatsBuffer", "(I)V", | |
| (void*)android_view_ThreadedRenderer_setProcessStatsBuffer}, | |
| {"nGetRenderThreadTid", "(J)I", (void*)android_view_ThreadedRenderer_getRenderThreadTid}, | |
| {"nCreateRootRenderNode", "()J", (void*)android_view_ThreadedRenderer_createRootRenderNode}, | |
| {"nCreateProxy", "(ZJ)J", (void*)android_view_ThreadedRenderer_createProxy}, | |
| {"nDeleteProxy", "(J)V", (void*)android_view_ThreadedRenderer_deleteProxy}, | |
| {"nLoadSystemProperties", "(J)Z", | |
| (void*)android_view_ThreadedRenderer_loadSystemProperties}, | |
| {"nSetName", "(JLjava/lang/String;)V", (void*)android_view_ThreadedRenderer_setName}, | |
| {"nSetSurface", "(JLandroid/view/Surface;Z)V", | |
| (void*)android_view_ThreadedRenderer_setSurface}, | |
| {"nSetSurfaceControl", "(JJ)V", (void*)android_view_ThreadedRenderer_setSurfaceControl}, | |
| {"nPause", "(J)Z", (void*)android_view_ThreadedRenderer_pause}, | |
| {"nSetStopped", "(JZ)V", (void*)android_view_ThreadedRenderer_setStopped}, | |
| {"nSetLightAlpha", "(JFF)V", (void*)android_view_ThreadedRenderer_setLightAlpha}, | |
| {"nSetLightGeometry", "(JFFFF)V", (void*)android_view_ThreadedRenderer_setLightGeometry}, | |
| {"nSetOpaque", "(JZ)V", (void*)android_view_ThreadedRenderer_setOpaque}, | |
| {"nSetColorMode", "(JI)F", (void*)android_view_ThreadedRenderer_setColorMode}, | |
| {"nSetTargetSdrHdrRatio", "(JF)V", | |
| (void*)android_view_ThreadedRenderer_setTargetSdrHdrRatio}, | |
| {"nSetSdrWhitePoint", "(JF)V", (void*)android_view_ThreadedRenderer_setSdrWhitePoint}, | |
| {"nSetIsHighEndGfx", "(Z)V", (void*)android_view_ThreadedRenderer_setIsHighEndGfx}, | |
| {"nSetIsLowRam", "(Z)V", (void*)android_view_ThreadedRenderer_setIsLowRam}, | |
| {"nSetIsSystemOrPersistent", "(Z)V", | |
| (void*)android_view_ThreadedRenderer_setIsSystemOrPersistent}, | |
| {"nSyncAndDrawFrame", "(J[JI)I", (void*)android_view_ThreadedRenderer_syncAndDrawFrame}, | |
| {"nDestroy", "(JJ)V", (void*)android_view_ThreadedRenderer_destroy}, | |
| {"nRegisterAnimatingRenderNode", "(JJ)V", | |
| (void*)android_view_ThreadedRenderer_registerAnimatingRenderNode}, | |
| {"nRegisterVectorDrawableAnimator", "(JJ)V", | |
| (void*)android_view_ThreadedRenderer_registerVectorDrawableAnimator}, | |
| {"nCreateTextureLayer", "(J)J", (void*)android_view_ThreadedRenderer_createTextureLayer}, | |
| {"nBuildLayer", "(JJ)V", (void*)android_view_ThreadedRenderer_buildLayer}, | |
| {"nCopyLayerInto", "(JJJ)Z", (void*)android_view_ThreadedRenderer_copyLayerInto}, | |
| {"nPushLayerUpdate", "(JJ)V", (void*)android_view_ThreadedRenderer_pushLayerUpdate}, | |
| {"nCancelLayerUpdate", "(JJ)V", (void*)android_view_ThreadedRenderer_cancelLayerUpdate}, | |
| {"nDetachSurfaceTexture", "(JJ)V", | |
| (void*)android_view_ThreadedRenderer_detachSurfaceTexture}, | |
| {"nDestroyHardwareResources", "(J)V", | |
| (void*)android_view_ThreadedRenderer_destroyHardwareResources}, | |
| {"nTrimMemory", "(I)V", (void*)android_view_ThreadedRenderer_trimMemory}, | |
| {"nOverrideProperty", "(Ljava/lang/String;Ljava/lang/String;)V", | |
| (void*)android_view_ThreadedRenderer_overrideProperty}, | |
| {"nFence", "(J)V", (void*)android_view_ThreadedRenderer_fence}, | |
| {"nStopDrawing", "(J)V", (void*)android_view_ThreadedRenderer_stopDrawing}, | |
| {"nNotifyFramePending", "(J)V", (void*)android_view_ThreadedRenderer_notifyFramePending}, | |
| {"nDumpProfileInfo", "(JLjava/io/FileDescriptor;I)V", | |
| (void*)android_view_ThreadedRenderer_dumpProfileInfo}, | |
| {"nDumpGlobalProfileInfo", "(Ljava/io/FileDescriptor;I)V", | |
| (void*)android_view_ThreadedRenderer_dumpGlobalProfileInfo}, | |
| {"setupShadersDiskCache", "(Ljava/lang/String;Ljava/lang/String;)V", | |
| (void*)android_view_ThreadedRenderer_setupShadersDiskCache}, | |
| {"nAddRenderNode", "(JJZ)V", (void*)android_view_ThreadedRenderer_addRenderNode}, | |
| {"nRemoveRenderNode", "(JJ)V", (void*)android_view_ThreadedRenderer_removeRenderNode}, | |
| {"nDrawRenderNode", "(JJ)V", (void*)android_view_ThreadedRendererd_drawRenderNode}, | |
| {"nSetContentDrawBounds", "(JIIII)V", | |
| (void*)android_view_ThreadedRenderer_setContentDrawBounds}, | |
| {"nForceDrawNextFrame", "(J)V", (void*)android_view_ThreadedRenderer_forceDrawNextFrame}, | |
| {"nSetPictureCaptureCallback", | |
| "(JLandroid/graphics/HardwareRenderer$PictureCapturedCallback;)V", | |
| (void*)android_view_ThreadedRenderer_setPictureCapturedCallbackJNI}, | |
| {"nSetASurfaceTransactionCallback", | |
| "(JLandroid/graphics/HardwareRenderer$ASurfaceTransactionCallback;)V", | |
| (void*)android_view_ThreadedRenderer_setASurfaceTransactionCallback}, | |
| {"nSetPrepareSurfaceControlForWebviewCallback", | |
| "(JLandroid/graphics/HardwareRenderer$PrepareSurfaceControlForWebviewCallback;)V", | |
| (void*)android_view_ThreadedRenderer_setPrepareSurfaceControlForWebviewCallback}, | |
| {"nSetFrameCallback", "(JLandroid/graphics/HardwareRenderer$FrameDrawingCallback;)V", | |
| (void*)android_view_ThreadedRenderer_setFrameCallback}, | |
| {"nSetFrameCommitCallback", "(JLandroid/graphics/HardwareRenderer$FrameCommitCallback;)V", | |
| (void*)android_view_ThreadedRenderer_setFrameCommitCallback}, | |
| {"nSetFrameCompleteCallback", | |
| "(JLandroid/graphics/HardwareRenderer$FrameCompleteCallback;)V", | |
| (void*)android_view_ThreadedRenderer_setFrameCompleteCallback}, | |
| {"nAddObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_addObserver}, | |
| {"nRemoveObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_removeObserver}, | |
| {"nCopySurfaceInto", | |
| "(Landroid/view/Surface;IIIILandroid/graphics/HardwareRenderer$CopyRequest;)V", | |
| (void*)android_view_ThreadedRenderer_copySurfaceInto}, | |
| {"nCreateHardwareBitmap", "(JII)Landroid/graphics/Bitmap;", | |
| (void*)android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode}, | |
| {"disableVsync", "()V", (void*)android_view_ThreadedRenderer_disableVsync}, | |
| {"nSetHighContrastText", "(Z)V", (void*)android_view_ThreadedRenderer_setHighContrastText}, | |
| {"nSetDebuggingEnabled", "(Z)V", (void*)android_view_ThreadedRenderer_setDebuggingEnabled}, | |
| {"nSetIsolatedProcess", "(Z)V", (void*)android_view_ThreadedRenderer_setIsolatedProcess}, | |
| {"nSetContextPriority", "(I)V", (void*)android_view_ThreadedRenderer_setContextPriority}, | |
| {"nAllocateBuffers", "(J)V", (void*)android_view_ThreadedRenderer_allocateBuffers}, | |
| {"nSetForceDark", "(JI)V", (void*)android_view_ThreadedRenderer_setForceDark}, | |
| {"nSetDisplayDensityDpi", "(I)V", | |
| (void*)android_view_ThreadedRenderer_setDisplayDensityDpi}, | |
| {"nInitDisplayInfo", "(IIFIJJZZZ)V", (void*)android_view_ThreadedRenderer_initDisplayInfo}, | |
| {"preload", "()V", (void*)android_view_ThreadedRenderer_preload}, | |
| {"isWebViewOverlaysEnabled", "()Z", | |
| (void*)android_view_ThreadedRenderer_isWebViewOverlaysEnabled}, | |
| {"nSetDrawingEnabled", "(Z)V", (void*)android_view_ThreadedRenderer_setDrawingEnabled}, | |
| {"nIsDrawingEnabled", "()Z", (void*)android_view_ThreadedRenderer_isDrawingEnabled}, | |
| {"nSetRtAnimationsEnabled", "(Z)V", | |
| (void*)android_view_ThreadedRenderer_setRtAnimationsEnabled}, | |
| {"nNotifyCallbackPending", "(J)V", | |
| (void*)android_view_ThreadedRenderer_notifyCallbackPending}, | |
| {"nNotifyExpensiveFrame", "(J)V", | |
| (void*)android_view_ThreadedRenderer_notifyExpensiveFrame}, | |
| {"nTrimCaches", "(I)V", (void*)android_view_ThreadedRenderer_trimCaches}, | |
| }; | |
| static JavaVM* mJvm = nullptr; | |
| static void attachRenderThreadToJvm(const char* name) { | |
| LOG_ALWAYS_FATAL_IF(!mJvm, "No jvm but we set the hook??"); | |
| JavaVMAttachArgs args; | |
| args.version = JNI_VERSION_1_4; | |
| args.name = name; | |
| args.group = NULL; | |
| JNIEnv* env; | |
| mJvm->AttachCurrentThreadAsDaemon(&env, (void*) &args); | |
| } | |
| int register_android_view_ThreadedRenderer(JNIEnv* env) { | |
| env->GetJavaVM(&mJvm); | |
| RenderThread::setOnStartHook(&attachRenderThreadToJvm); | |
| jclass hardwareRenderer = FindClassOrDie(env, | |
| "android/graphics/HardwareRenderer"); | |
| gHardwareRenderer.clazz = reinterpret_cast<jclass>(env->NewGlobalRef(hardwareRenderer)); | |
| gHardwareRenderer.invokePictureCapturedCallback = GetStaticMethodIDOrDie(env, hardwareRenderer, | |
| "invokePictureCapturedCallback", | |
| "(JLandroid/graphics/HardwareRenderer$PictureCapturedCallback;)V"); | |
| jclass aSurfaceTransactionCallbackClass = | |
| FindClassOrDie(env, "android/graphics/HardwareRenderer$ASurfaceTransactionCallback"); | |
| gASurfaceTransactionCallback.onMergeTransaction = | |
| GetMethodIDOrDie(env, aSurfaceTransactionCallbackClass, "onMergeTransaction", "(JJJ)Z"); | |
| jclass prepareSurfaceControlForWebviewCallbackClass = FindClassOrDie( | |
| env, "android/graphics/HardwareRenderer$PrepareSurfaceControlForWebviewCallback"); | |
| gPrepareSurfaceControlForWebviewCallback.prepare = | |
| GetMethodIDOrDie(env, prepareSurfaceControlForWebviewCallbackClass, "prepare", "()V"); | |
| jclass frameCallbackClass = FindClassOrDie(env, | |
| "android/graphics/HardwareRenderer$FrameDrawingCallback"); | |
| gFrameDrawingCallback.onFrameDraw = | |
| GetMethodIDOrDie(env, frameCallbackClass, "onFrameDraw", | |
| "(IJ)Landroid/graphics/HardwareRenderer$FrameCommitCallback;"); | |
| jclass frameCommitClass = | |
| FindClassOrDie(env, "android/graphics/HardwareRenderer$FrameCommitCallback"); | |
| gFrameCommitCallback.onFrameCommit = | |
| GetMethodIDOrDie(env, frameCommitClass, "onFrameCommit", "(Z)V"); | |
| jclass frameCompleteClass = FindClassOrDie(env, | |
| "android/graphics/HardwareRenderer$FrameCompleteCallback"); | |
| gFrameCompleteCallback.onFrameComplete = | |
| GetMethodIDOrDie(env, frameCompleteClass, "onFrameComplete", "()V"); | |
| jclass copyRequest = FindClassOrDie(env, "android/graphics/HardwareRenderer$CopyRequest"); | |
| gCopyRequest.onCopyFinished = GetMethodIDOrDie(env, copyRequest, "onCopyFinished", "(I)V"); | |
| gCopyRequest.getDestinationBitmap = | |
| GetMethodIDOrDie(env, copyRequest, "getDestinationBitmap", "(II)J"); | |
| void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE); | |
| fromSurface = (ANW_fromSurface)dlsym(handle_, "ANativeWindow_fromSurface"); | |
| LOG_ALWAYS_FATAL_IF(fromSurface == nullptr, | |
| "Failed to find required symbol ANativeWindow_fromSurface!"); | |
| return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); | |
| } | |
| }; // namespace android |