/* * 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. */ #undef LOG_TAG #define LOG_TAG "HardwareBufferRenderer" #define ATRACE_TAG ATRACE_TAG_VIEW #include <GraphicsJNI.h> #include <RootRenderNode.h> #include <TreeInfo.h> #include <android-base/unique_fd.h> #include <android/native_window.h> #include <nativehelper/JNIPlatformHelp.h> #include <renderthread/CanvasContext.h> #include <renderthread/RenderProxy.h> #include <renderthread/RenderThread.h> #include "HardwareBufferHelpers.h" #include "JvmErrorReporter.h" namespace android { using namespace android::uirenderer; using namespace android::uirenderer::renderthread; struct { jclass clazz; jmethodID invokeRenderCallback; } gHardwareBufferRendererClassInfo; static RenderCallback createRenderCallback(JNIEnv* env, jobject releaseCallback) { if (releaseCallback == nullptr) return nullptr; 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(releaseCallback)); return [globalCallbackRef](android::base::unique_fd&& fd, int status) { globalCallbackRef->env()->CallStaticVoidMethod( gHardwareBufferRendererClassInfo.clazz, gHardwareBufferRendererClassInfo.invokeRenderCallback, globalCallbackRef->object(), reinterpret_cast<jint>(fd.release()), reinterpret_cast<jint>(status)); }; } static long android_graphics_HardwareBufferRenderer_createRootNode(JNIEnv* env, jobject) { auto* node = new RootRenderNode(std::make_unique<JvmErrorReporter>(env)); node->incStrong(nullptr); node->setName("RootRenderNode"); return reinterpret_cast<jlong>(node); } static void android_graphics_hardwareBufferRenderer_destroyRootNode(JNIEnv*, jobject, jlong renderNodePtr) { auto* node = reinterpret_cast<RootRenderNode*>(renderNodePtr); node->destroy(); } static long android_graphics_HardwareBufferRenderer_create(JNIEnv* env, jobject, jobject buffer, jlong renderNodePtr) { auto* hardwareBuffer = HardwareBufferHelpers::AHardwareBuffer_fromHardwareBuffer(env, buffer); auto* rootRenderNode = reinterpret_cast<RootRenderNode*>(renderNodePtr); ContextFactoryImpl factory(rootRenderNode); auto* proxy = new RenderProxy(false, rootRenderNode, &factory); proxy->setHardwareBuffer(hardwareBuffer); return (jlong)proxy; } static void HardwareBufferRenderer_destroy(jlong renderProxy) { auto* proxy = reinterpret_cast<RenderProxy*>(renderProxy); delete proxy; } static SkMatrix createMatrixFromBufferTransform(SkScalar width, SkScalar height, int transform) { switch (transform) { case ANATIVEWINDOW_TRANSFORM_ROTATE_90: return SkMatrix::MakeAll(0, -1, height, 1, 0, 0, 0, 0, 1); case ANATIVEWINDOW_TRANSFORM_ROTATE_180: return SkMatrix::MakeAll(-1, 0, width, 0, -1, height, 0, 0, 1); case ANATIVEWINDOW_TRANSFORM_ROTATE_270: return SkMatrix::MakeAll(0, 1, 0, -1, 0, width, 0, 0, 1); default: ALOGE("Invalid transform provided. Transform should be validated from" "the java side. Leveraging identity transform as a fallback"); [[fallthrough]]; case ANATIVEWINDOW_TRANSFORM_IDENTITY: return SkMatrix::I(); } } static int android_graphics_HardwareBufferRenderer_render(JNIEnv* env, jobject, jlong renderProxy, jint transform, jint width, jint height, jlong colorspacePtr, jobject consumer) { auto* proxy = reinterpret_cast<RenderProxy*>(renderProxy); auto skWidth = static_cast<SkScalar>(width); auto skHeight = static_cast<SkScalar>(height); auto matrix = createMatrixFromBufferTransform(skWidth, skHeight, transform); auto colorSpace = GraphicsJNI::getNativeColorSpace(colorspacePtr); proxy->setHardwareBufferRenderParams(HardwareBufferRenderParams( width, height, matrix, colorSpace, createRenderCallback(env, consumer))); 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); return proxy->syncAndDrawFrame(); } static void android_graphics_HardwareBufferRenderer_setLightGeometry(JNIEnv*, jobject, jlong renderProxyPtr, jfloat lightX, jfloat lightY, jfloat lightZ, jfloat lightRadius) { auto* proxy = reinterpret_cast<RenderProxy*>(renderProxyPtr); proxy->setLightGeometry((Vector3){lightX, lightY, lightZ}, lightRadius); } static void android_graphics_HardwareBufferRenderer_setLightAlpha(JNIEnv* env, jobject, jlong renderProxyPtr, jfloat ambientShadowAlpha, jfloat spotShadowAlpha) { auto* proxy = reinterpret_cast<RenderProxy*>(renderProxyPtr); proxy->setLightAlpha((uint8_t)(255 * ambientShadowAlpha), (uint8_t)(255 * spotShadowAlpha)); } static jlong android_graphics_HardwareBufferRenderer_getFinalizer() { return static_cast<jlong>(reinterpret_cast<uintptr_t>(&HardwareBufferRenderer_destroy)); } // ---------------------------------------------------------------------------- // JNI Glue // ---------------------------------------------------------------------------- const char* const kClassPathName = "android/graphics/HardwareBufferRenderer"; static const JNINativeMethod gMethods[] = { {"nCreateHardwareBufferRenderer", "(Landroid/hardware/HardwareBuffer;J)J", (void*)android_graphics_HardwareBufferRenderer_create}, {"nRender", "(JIIIJLjava/util/function/Consumer;)I", (void*)android_graphics_HardwareBufferRenderer_render}, {"nCreateRootRenderNode", "()J", (void*)android_graphics_HardwareBufferRenderer_createRootNode}, {"nSetLightGeometry", "(JFFFF)V", (void*)android_graphics_HardwareBufferRenderer_setLightGeometry}, {"nSetLightAlpha", "(JFF)V", (void*)android_graphics_HardwareBufferRenderer_setLightAlpha}, {"nGetFinalizer", "()J", (void*)android_graphics_HardwareBufferRenderer_getFinalizer}, {"nDestroyRootRenderNode", "(J)V", (void*)android_graphics_hardwareBufferRenderer_destroyRootNode}}; int register_android_graphics_HardwareBufferRenderer(JNIEnv* env) { jclass hardwareBufferRendererClazz = FindClassOrDie(env, "android/graphics/HardwareBufferRenderer"); gHardwareBufferRendererClassInfo.clazz = reinterpret_cast<jclass>(env->NewGlobalRef(hardwareBufferRendererClazz)); gHardwareBufferRendererClassInfo.invokeRenderCallback = GetStaticMethodIDOrDie(env, hardwareBufferRendererClazz, "invokeRenderCallback", "(Ljava/util/function/Consumer;II)V"); HardwareBufferHelpers::init(); return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); } } // namespace android