【Rive】rive-android源码分析

1 前言

​ 本文基于 rive-android 10.1.0 进行源码分析,主要介绍 Rive 的渲染类型、RendererType 透传流程、Surface 透传流程、渲染流程、启动渲染流程、暂停渲染流程等内容。

​ rive-android 类图框架如下。图中,蓝色的类表示 rive-android 中 Kotlin 代码,绿色的类表示 rive-android 中 C++ 代码,橙色的类表示 rive-runtime 中代码(下同)。本文只解读 rive-android 的源码,不解读 rive-runtime 的源码。

​ Rive 相关应用参考以下内容。

2 渲染类型

​ rive-android 10.0.0 之前的版本,有三种渲染类型,分别是:Skia、Rive、Canvas,默认是 Skia 类型。10.0.0 版本开始舍弃了 Skia 渲染类型,默认是 Rive 类型。Skia 和 Rive 渲染类型底层都是基于 EGL 环境进行离屏渲染。

​ RenderType 类源码如下。

​ app.rive.runtime.kotlin.core.RendererType.kt

Kotlin 复制代码
enum class RendererType(val value: Int) {
    Rive(0),
    Canvas(1);

    companion object {
        fun fromIndex(index: Int): RendererType {
            val maxIndex = entries.size
            ...
            return entries[index]
        }
    }
}

3 RendererType 透传流程

​ 本节将介绍 RendererType 如何一步一步透传下去,直到最终创建 DrawableThreadState 对象。本节中,读者需要重点关注 RendererType 是如何在各个类之间传递的。RendererType 透传流程图如下。

3.1 设置 RendererType 的源头

​ 用户在初始化 Rive 环境时,可以指定 RenderType,如下。

Kotlin 复制代码
Rive.init(applicationContext, defaultRenderer = RendererType.Rive)

​ 也可以在布局文件中指定 RenderType(如果想使用 Winscope 或 Perfetto 查看 Trace,可以配置 riveTraceAnimations 参数),如下。

XML 复制代码
<app.rive.runtime.kotlin.RiveAnimationView
  app:riveRenderer="Rive"
  app:riveTraceAnimations="true"
  ... />

​ 无论是哪种方式,在 RiveAnimationView 对象被创建时,渲染类型会保存在 rendererAttributes 对象的 rendererType 属性中(rendererAttributes 是 RendererAttributes 类型)。

3.2 创建 RiveArtboardRenderer

​ RiveAnimationView 继承了 RiveTextureView,RiveTextureView 继承了 TextureView,并重写了其 onAttachedToWindow 函数,如下。

​ app.rive.runtime.kotlin.RiveTextureView.kt

Kotlin 复制代码
@CallSuper
override fun onAttachedToWindow() {
	super.onAttachedToWindow()
	surfaceTextureListener = this
	isOpaque = false
	renderer = createRenderer().apply { make() }
}

​ 在 RiveAnimationView 中重写了 createRenderer 函数,如下。

​ app.rive.runtime.kotlin.RiveAnimationView.kt

Kotlin 复制代码
override fun createRenderer(): Renderer {
	return RiveArtboardRenderer(
		trace = rendererAttributes.riveTraceAnimations,
		controller = controller,
		rendererType = rendererAttributes.rendererType,
	)
}

​ RiveArtboardRenderer 只有主构造函数,没有次要构造函数,如下。关于主构造函数和次要构造函数的介绍详见 → 类和对象

​ app.rive.runtime.kotlin.renderers.RiveArtboardRenderer.kt

Kotlin 复制代码
open class RiveArtboardRenderer(
    trace: Boolean = false,
    rendererType: RendererType = Rive.defaultRendererType,
    private var controller: RiveFileController,
) : Renderer(rendererType, trace)

​ Renderer 的也只有主构造函数,没有次要构造函数,如下。

​ app.rive.runtime.kotlin.renderers.Renderer.kt

Kotlin 复制代码
abstract class Renderer(
    @get:VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    var type: RendererType = Rive.defaultRendererType,
    val trace: Boolean = false
) : NativeObject(NULL_POINTER), Choreographer.FrameCallback

3.3 创建 JNIRenderer

​ 我们再回到 RiveTextureView 的 onAttachedToWindow 函数中,在 createRenderer 之后,调用了 Renderer 的 make 函数。make 函数的源码如下。cppPointer 是在 NativeObject 中定义的 Long 类型变量,对应 C++ 中的指针变量,指向的是 JNI 中 JNIRenderer 对象。在 make 函数中又调用了 constructor 函数,它在 JNI 中有实现。(注意:这里的 constructor 不是 Renderer 的构造函数,因为其前面加了 fun,构造函数前面不能加 fun)

​ app.rive.runtime.kotlin.renderers.Renderer.kt

Kotlin 复制代码
@CallSuper
open fun make() {
	if (!hasCppObject) {
		cppPointer = constructor(trace, type.value)
		refs.incrementAndGet()
	}
}

/** Instantiates JNIRenderer in C++ */
private external fun constructor(trace: Boolean, type: Int): Long

​ 全局搜索 "Renderer_constructor",找到 JNI 中的 constructor 函数的实现如下。

​ bindings/bindings_renderer.cpp

cpp 复制代码
JNIEXPORT jlong JNICALL
Java_app_rive_runtime_kotlin_renderers_Renderer_constructor(JNIEnv* env, jobject ktRenderer,
	jboolean trace, jint type)
{
	RendererType rendererType = static_cast<RendererType>(type);
	JNIRenderer* renderer = new JNIRenderer(ktRenderer, trace, rendererType);
	...
	return (jlong)renderer;
}

​ JNIRenderer 的构造函数如下,这里会创建 RefWorker 对象保存在 m_worker 中,创建 ITracer 对象保存在 m_tracer 中。如果 trace 为 false,将创建 NoopTracer 对象,其 beginSection、endSection 函数都是空实现;如果 trace 为 true,将创建 Tracer 对象,其 beginSection 函数将调用 ATrace_beginSection,endSection 函数将调用 ATrace_endSection(详见 aosp 中的 frameworks/base/native/android/trace.cpp),这时用户就可以使用 Winscope 或 Perfetto 查看 Trace 了。

​ models/jni_renderer.cpp

cpp 复制代码
JNIRenderer::JNIRenderer(jobject ktRenderer, bool trace, const RendererType rendererType) :
    m_worker(RefWorker::CurrentOrFallback(rendererType)),
    m_ktRenderer(GetJNIEnv()->NewGlobalRef(ktRenderer)),
    m_tracer(getTracer(trace))
{}

3.4 创建 RefWorker

​ RefWorker::CurrentOrFallback 的源码如下。

​ helpers/work_ref.cpp

cpp 复制代码
rcp<RefWorker> RefWorker::CurrentOrFallback(RendererType rendererType)
{
    rcp<RefWorker> currentOrFallback;
    switch (rendererType)
    {
        case RendererType::None:
            assert(false);
            break;
        case RendererType::Rive:
            currentOrFallback = RiveWorker();
            break;
        case RendererType::Canvas:
            currentOrFallback = CanvasWorker();
            break;
    }
    if (currentOrFallback == nullptr)
    {
        currentOrFallback = CanvasWorker();
    }
    return currentOrFallback;
}

​ RiveWorker 和 CanvasWorker 函数实现如下。

​ helpers/work_ref.cpp

cpp 复制代码
rcp<RefWorker> RefWorker::RiveWorker()
{
    static enum class RiveRendererSupport { unknown, no, yes } s_isSupported;
    static std::unique_ptr<RefWorker> s_riveWorker;
    ...
    if (s_isSupported == RiveRendererSupport::unknown)
    {
        std::unique_ptr<RefWorker> candidateWorker(new RefWorker(RendererType::Rive));
        candidateWorker->runAndWait(
            [](rive_android::DrawableThreadState* threadState) {
                PLSThreadState* plsThreadState = static_cast<PLSThreadState*>(threadState);
                s_isSupported = plsThreadState->renderContext() != nullptr ? RiveRendererSupport::yes : RiveRendererSupport::no;
            });
        if (s_isSupported == RiveRendererSupport::yes)
        {
            s_riveWorker = std::move(candidateWorker);
        }
        ...
    }
    if (s_riveWorker != nullptr)
    {
        ++s_riveWorker->m_externalRefCount;
    }
    return rcp(s_riveWorker.get());
}

rcp<RefWorker> RefWorker::CanvasWorker()
{
    if (s_canvasWorker == nullptr)
    {
        s_canvasWorker = std::unique_ptr<RefWorker>(new RefWorker(RendererType::Canvas));
    }
    ++s_canvasWorker->m_externalRefCount;
    return rcp(s_canvasWorker.get());
}

​ RefWorker 继承 WorkerThread,如下。

​ helpers/work_ref.hpp

cpp 复制代码
explicit RefWorker(const RendererType rendererType) :
	WorkerThread(RendererName(rendererType), Affinity::None, rendererType)
{}

3.5 创建 WorkThread

​ WorkThread 的构造函数如下,可以看到这里创建了一个线程,用于处理渲染任务。

​ helpers/worker_thread.hpp

cpp 复制代码
WorkerThread(const char* name, Affinity affinity, const RendererType rendererType) :
	m_RendererType(rendererType),
	mName(name),
	mAffinity(affinity),
	mWorkMutex{}
{
	mThread = std::thread([this]() { threadMain(); });
}

​ threadMain 函数的实现如下。首先通过 MakeThreadState 函数创建 DrawableThreadState 对象;接着进入 for 无限循环体中,如果 mWorkQueue 为空(mWorkQueue 中的元素是 function<void(DrawableThreadState*) 类型的函数指针),线程将处于 wait 状态,否则从工作队列中取出函数指针元素,并执行函数;如果从工作队列中取出的函数指针为空,将结束线程。

​ helpers/worker_thread.hpp

cpp 复制代码
void threadMain()
{
	setAffinity(mAffinity);
	...
	m_threadState = MakeThreadState(m_RendererType);
	std::unique_lock lock(mWorkMutex);
	for (;;)
	{
		while (mWorkQueue.empty())
		{
			m_workPushedCondition.wait(mWorkMutex);
		}
		Work work = mWorkQueue.front();
		mWorkQueue.pop();

		if (!work)
		{
			// A null function is a special token that tells the thread to terminate.
			break;
		}

		lock.unlock();
		work(m_threadState.get());
		lock.lock();

		++m_lastCompletedWorkID;
		m_workedCompletedCondition.notify_all();
	}
	m_threadState.reset();
	DetachThread();
}

3.6 创建 DrawableThreadState

​ MakeThreadState 函数的实现如下。

​ helpers/worker_thread.cpp

cpp 复制代码
std::unique_ptr<DrawableThreadState> WorkerThread::MakeThreadState(const RendererType type)
{
    switch (type)
    {
        case RendererType::Canvas:
            return std::make_unique<CanvasThreadState>();
        default:
        case RendererType::Rive:
            return std::make_unique<PLSThreadState>();
    }
}

​ CanvasThreadState 继承 DrawableThreadState,PLSThreadState 继承 EGLThreadState,EGLThreadState 继承 DrawableThreadState(在 rive-android 10.0.0 之前的版本,还有 SkiaThreadState 也继承 EGLThreadState,它对应的 RenderType 是 Skia)。

3.6.1 DrawableThreadState、EGLThreadState、CanvasThreadState 接口定义

​ DrawableThreadState、EGLThreadState、CanvasThreadState 的接口定义如下。

​ helpers/thread_state_egl.hpp

cpp 复制代码
class DrawableThreadState
{
public:
    virtual ~DrawableThreadState(){};
    virtual void swapBuffers() = 0;
};

class EGLThreadState : public DrawableThreadState
{
public:
    EGLThreadState();

    virtual ~EGLThreadState() = 0;

    EGLSurface createEGLSurface(ANativeWindow*);

    virtual void destroySurface(EGLSurface) = 0;

    virtual void makeCurrent(EGLSurface) = 0;

    void swapBuffers() override;

protected:
    EGLSurface m_currentSurface = EGL_NO_SURFACE;
    EGLDisplay m_display = EGL_NO_DISPLAY;
    EGLContext m_context = EGL_NO_CONTEXT;
    EGLConfig m_config = static_cast<EGLConfig>(0);
};

class CanvasThreadState : public DrawableThreadState
{
public:
    void swapBuffers() override {}
};

3.6.2 创建 EGLThreadState

​ EGLThreadState 的构造函数源码如下,它实现了 EGL 环境创建 5 步中的前 3步,剩下 2 步在 PLSThreadState 的构造函数中实现。EGL 环境创建详见 → EGL+FBO离屏渲染

​ helpers/thread_state_egl.cpp

cpp 复制代码
EGLThreadState::EGLThreadState()
{
    // 创建 EGLDisplay
    m_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    ...
    if (!eglInitialize(m_display, 0, 0))
    {
        ...
        return;
    }

    // 创建 EGLConfig
    const EGLint configAttributes[] = {...};
    ...
    eglChooseConfig(m_display, configAttributes, supportedConfigs.data(), num_configs, &num_configs);
    ...

	// 创建 EGLContext
    m_context = eglCreateContext(m_display, m_config, nullptr, contextAttributes);
    ...
}

3.6.3 创建 PLSThreadState

​ PLSThreadState 的构造函数源码如下,它实现了 EGL 环境创建 5 步中的后 2步。eglCreatePbufferSurface 用于创建一个离屏渲染的 EGL 表面,Pbuffer 表面是一个虚拟的离屏缓冲区,可以在其中进行渲染操作,而不直接与屏幕交互。rive::gpu::RenderContextGLImpl::MakeContext 的源码不在 rive-android 代码库中,在 rive-runtime 代码库中,详见 → render_context_gl_impl.cpp

​ helpers/thread_state_psl.cpp

cpp 复制代码
PLSThreadState::PLSThreadState()
{
    // 创建 EGLSurface
    const EGLint PbufferAttrs[] = {...};
    m_backgroundSurface = eglCreatePbufferSurface(m_display, m_config, PbufferAttrs);
    ...

    // 绑定 EGLSurface 和 EGLContext 到显示设备 (EGLDisplay)
    eglMakeCurrent(m_display, m_backgroundSurface, m_backgroundSurface, m_context);
    m_currentSurface = m_backgroundSurface;

    m_renderContext = rive::gpu::RenderContextGLImpl::MakeContext();
}

4 Surface 透传流程

​ 本节将介绍 Surface 如何一步一步透传下去,直到最终创建 rive::RiveRenderer 对象或 CanvasRenderer 对象。本节中,读者需要重点关注 Surface 是如何在各个类之间传递的。Surface 透传流程图如下。

4.1 Surface 创建的源头

​ 在 RiveTextureView 的 onSurfaceTextureAvailable 方法中,创建了 Surface 如下。

​ app.rive.runtime.kotlin.RiveTextureView.kt

Kotlin 复制代码
@CallSuper
override fun onSurfaceTextureAvailable(
	surfaceTexture: SurfaceTexture, width: Int, height: Int) {
	if (this::viewSurface.isInitialized) {
		viewSurface.release()
	}
	viewSurface = Surface(surfaceTexture)
	renderer?.apply {
		stop()
		setSurface(viewSurface)
	}
}

4.2 Renderer.setSurface

​ Renderer 的 setSurface 函数源码如下。cppPointer 是在 NativeObject 中定义的 Long 类型变量,对应 C++ 中的指针变量,指向的是 JNI 中 JNIRenderer 对象。

​ app.rive.runtime.kotlin.renderers.Renderer.kt

Kotlin 复制代码
fun setSurface(surface: Surface) {
	cppSetSurface(surface, cppPointer)
	isAttached = true
	start()
}

4.3 JNI cppSetSurface

​ 全局搜索 Renderer_cppSetSurface,找到 JNI 层 cppSetSurface 函数的实现如下。对于 Rive 渲染类型,将创建一个 ANativeWindow 指针(ANativeWindow_fromSurface 函数详见 aosp 中的 frameworks/base/native/android/native_window_jni.cpp),并将 ANativeWindow 指针传递到 JNIRenderer 中;对于 Canvas 渲染类型,直接将 Surface 传递到 JNIRenderer 中。

​ bindings/bindings_renderer.cpp

cpp 复制代码
JNIEXPORT void JNICALL
Java_app_rive_runtime_kotlin_renderers_Renderer_cppSetSurface(JNIEnv* env, jobject,
	jobject surface, jlong rendererRef)
{
	JNIRenderer* renderer = reinterpret_cast<JNIRenderer*>(rendererRef);
	if (renderer->rendererType() != RendererType::Canvas)
	{
		ANativeWindow* surfaceWindow = ANativeWindow_fromSurface(env, surface);
		reinterpret_cast<JNIRenderer*>(rendererRef) ->setSurface(surfaceWindow);
		if (surfaceWindow)
		{
			ANativeWindow_release(surfaceWindow);
		}
	}
	else
	{
		renderer->setSurface(surface);
	}
}

4.4 JNIRenderer::setSurface

​ JNIRenderer::setSurface 的源码如下。m_worker 是 RefWorker 类的实例,RefWorker 继承 WorkThread,WorkThread 中创建了工作线程(见 3.5 节),并且有个 run 函数,m_worker->run 表示把任务提交到 WorkThread 中的工作线程中执行。

​ models/jni_renderer.cpp

cpp 复制代码
void JNIRenderer::setSurface(SurfaceVariant surface)
{
    SurfaceVariant oldSurface = m_surface;
    acquireSurface(surface);
    m_worker->run([this, oldSurface](DrawableThreadState* threadState) mutable {
        m_workerThreadID = std::this_thread::get_id();
        ...
        if (m_surface.index() > 0)
        {
            m_workerImpl = WorkerImpl::Make(m_surface, threadState, m_worker->rendererType());
        }
    });
}

4.5 WorkerImpl::Make

​ models/worker_impl.cpp

cpp 复制代码
std::unique_ptr<WorkerImpl> WorkerImpl::Make(SurfaceVariant surface,
    DrawableThreadState* threadState, const RendererType type)
{
    ...
    bool success = false;
    std::unique_ptr<WorkerImpl> impl;
    switch (type)
    {
        case RendererType::Rive:
        {
            ANativeWindow* window = std::get<ANativeWindow*>(surface);
            impl = std::make_unique<PLSWorkerImpl>(window, threadState, &success);
            break;
        }
        case RendererType::Canvas:
        {
            jobject ktSurface = std::get<jobject>(surface);
            impl = std::make_unique<CanvasWorkerImpl>(ktSurface, &success);
        }
        default:
            break;
    }
    ...
    return impl;
}

4.6 创建 WorkerImpl

​ PLSWorkerImpl 继承 EGLWorkerImpl,EGLWorkerImpl 和 CanvasWorkerImpl 继承 WorkerImpl。

4.6.1 WorkerImpl 接口定义

​ models/worker_impl.hpp

cpp 复制代码
class WorkerImpl
{
public:
    static std::unique_ptr<WorkerImpl> Make(SurfaceVariant, DrawableThreadState*, const RendererType);

    void start(jobject ktRenderer, std::chrono::high_resolution_clock::time_point);

    void stop();

    void doFrame(ITracer*, DrawableThreadState*, jobject ktRenderer, std::chrono::high_resolution_clock::time_point);

    virtual void prepareForDraw(DrawableThreadState*) const = 0;

    virtual void destroy(DrawableThreadState*) = 0;

    virtual void flush(DrawableThreadState*) const = 0;

    virtual rive::Renderer* renderer() const = 0;

protected:
    jclass m_ktRendererClass = nullptr;
    jmethodID m_ktDrawCallback = nullptr;
    jmethodID m_ktAdvanceCallback = nullptr;
    std::chrono::high_resolution_clock::time_point m_lastFrameTime;
    bool m_isStarted = false;
};

4.6.2 创建 EGLWorkerImpl

​ EGLWorkerImpl 的构造函数如下。

​ models/worker_impl.hpp

cpp 复制代码
class EGLWorkerImpl : public WorkerImpl
{
...
protected:
    EGLWorkerImpl(struct ANativeWindow* window, DrawableThreadState* threadState, bool* success)
    {
        *success = false;
        auto eglThreadState = static_cast<EGLThreadState*>(threadState);
        m_eglSurface = eglThreadState->createEGLSurface(window);
        if (m_eglSurface == EGL_NO_SURFACE)
            return;
        *success = true;
    }
...
};

​ EGLThreadState::createEGLSurface 函数源码如下。eglCreateWindowSurface 用于创建一个与屏幕窗口相关的 EGL 表面,这个表面通常与设备的窗口系统交互,使得 OpenGL ES 渲染的内容能够显示在屏幕上(补充:eglCreatePbufferSurface 用于创建一个离屏渲染的 EGL 表面)。

​ helpers/thread_state_egl.cpp

cpp 复制代码
EGLSurface EGLThreadState::createEGLSurface(ANativeWindow* window)
{
    if (!window)
    {
        return EGL_NO_SURFACE;
    }
    ...
    // 创建一个与屏幕窗口相关的 EGL 表面, 这个表面通常与设备的窗口系统交, 使得 OpenGL ES 渲染的内容能够显示在屏幕上
    auto res = eglCreateWindowSurface(m_display, m_config, window, nullptr);
    ...
    return res;
}

4.6.3 创建 PLSWorkerImpl

​ PLSWorkerImpl 的构造函数如下,最终创建了 rive::RiveRenderer 对象,其源码不在 rive-android 代码库中,在 rive-runtime 代码库中,详见 → rive_renderer.cpp

​ models/worker_impl.cpp

cpp 复制代码
PLSWorkerImpl::PLSWorkerImpl(struct ANativeWindow* window, DrawableThreadState* threadState, bool* success) :
    EGLWorkerImpl(window, threadState, success)
{
    if (!success)
    {
        return;
    }

    auto eglThreadState = static_cast<EGLThreadState*>(threadState);

    eglThreadState->makeCurrent(m_eglSurface);
    rive::gpu::RenderContext* renderContext = PLSWorkerImpl::PlsThreadState(eglThreadState)->renderContext();
    if (renderContext == nullptr)
    {
        return; // PLS was not supported.
    }
    int width = ANativeWindow_getWidth(window); // 获取窗口宽度
    int height = ANativeWindow_getHeight(window); // 获取窗口高度
    GLint sampleCount; // 多重采样数
    glBindFramebuffer(GL_FRAMEBUFFER, 0); // 绑定默认帧缓冲区
    glGetIntegerv(GL_SAMPLES, &sampleCount); // 查询当前的多重采样数
    // 创建一个基于 GLES 的帧缓冲区渲染目标,用于离屏渲染
    m_renderTarget = rive::make_rcp<rive::gpu::FramebufferRenderTargetGL>(width, height, 0, sampleCount);
    // 初始化 Rive 的 PLS 渲染器
    m_plsRenderer = std::make_unique<rive::RiveRenderer>(renderContext);
    *success = true;
}

​ PLSThreadState::makeCurrent 函数的实现如下,eglMakeCurrent 是 EGL 环境创建的最后一步,作用是绑定 EGLSurface 和 EGLContext 到显示设备(EGLDisplay)。

​ helpers/thread_state_pls.cpp

cpp 复制代码
void PLSThreadState::makeCurrent(EGLSurface eglSurface)
{
    if (eglSurface == m_currentSurface)
    {
        return;
    }
	...
    // 绑定 EGLSurface 和 EGLContext 到显示设备 (EGLDisplay)
    eglMakeCurrent(m_display, eglSurface, eglSurface, m_context);
    m_currentSurface = eglSurface;
	...
}

4.6.4 创建 CanvasWorkerImpl

​ CanvasWorkerImpl 的构造函数如下。这里主要创建了 CanvasRenderer 对象,保存在 m_canvasRenderer 中,CanvasRenderer 继承 rive::Renderer,其源码不在 rive-android 代码库中,在 rive-runtime 代码库中,详见 → renderer.hpprenderer.cpp;接着通过 NewGlobalRef(ktSurface) 将传入的局部引用 ktSurface 转换为全局引用,并存储在 m_ktSurface 中(全局引用会阻止 Java 垃圾回收器回收该对象,直到显式释放)。

​ models/worker_impl.hpp

cpp 复制代码
class CanvasWorkerImpl : public WorkerImpl
{
public:
    CanvasWorkerImpl(jobject ktSurface, bool* success) :
        m_canvasRenderer{std::make_unique<CanvasRenderer>()}
    {
        m_ktSurface = GetJNIEnv()->NewGlobalRef(ktSurface);
        *success = true;
    }

private:
    std::unique_ptr<CanvasRenderer> m_canvasRenderer;
    jobject m_ktSurface = nullptr;
};

5 渲染流程

​ 渲染流程图如下。

5.1 Renderer.doFrame

​ Renderer 实现了 Choreographer.FrameCallback 接口,重写了其 doFrame 方法,其 doFrame 和 scheduleFrame 方法源码如下。

​ app.rive.runtime.kotlin.renderers.Renderer.kt

Kotlin 复制代码
@CallSuper
override fun doFrame(frameTimeNanos: Long) {
	if (isPlaying) {
		cppDoFrame(cppPointer)
		scheduleFrame()
	}
}

open fun scheduleFrame() {
	Handler(Looper.getMainLooper()).post {
		Choreographer.getInstance().postFrameCallback(this@Renderer)
	}
}

5.2 JNI cppDoFrame

​ 全局搜索 "Renderer_cppDoFrame",找到 JNI 中的 cppDoFrame 函数的实现如下。

​ bindings/bindings_renderer.cpp

cpp 复制代码
JNIEXPORT void JNICALL
Java_app_rive_runtime_kotlin_renderers_Renderer_cppDoFrame(JNIEnv*, jobject, jlong rendererRef)
{
	reinterpret_cast<JNIRenderer*>(rendererRef)->doFrame();
}

5.3 JNIRenderer::doFrame

​ JNIRenderer::doFrame 的源码如下。m_worker 是 RefWorker 类的实例,RefWorker 继承 WorkThread,WorkThread 中创建了工作线程(见 3.5 节),并且有个 run 函数,m_worker->run 表示把任务提交到 WorkThread 中的工作线程中执行。m_workerImpl 是 4.6 节创建的 WorkerImpl 对象(可能是 PLSWorkerImpl 或 CanavsWorkerImpl)。

​ models/jni_renderer.cpp

cpp 复制代码
void JNIRenderer::doFrame()
{
    if (m_numScheduledFrames >= kMaxScheduledFrames)
    {
        return;
    }

    m_worker->run([this](DrawableThreadState* threadState) {
        if (!m_workerImpl)
            return;
        auto now = std::chrono::high_resolution_clock::now();
        m_workerImpl->doFrame(m_tracer, threadState, m_ktRenderer, now);
        m_numScheduledFrames--;
        calculateFps(now);
    });
    m_numScheduledFrames++;
}

5.4 WorkerImpl::doFrame

5.4.1 doFrame

​ WorkerImpl::doFrame 的源码如下。tracer 是性能追踪器,用于测量各阶段耗时,它在 JNIRenderer 中创建(见 3.3 节),这里有 3 个追踪标签,分别是 "draw() "、"flush() "、"swapBuffers()"。m_ktAdvanceCallback 在 start 函数中定义,指向 kotlin 中 Renderer.advance 函数;m_ktDrawCallback 在 start 函数中定义,指向 kotlin 中 Renderer.draw 函数。

​ models/worker_impl.cpp

cpp 复制代码
void WorkerImpl::doFrame(ITracer* tracer, DrawableThreadState* threadState, jobject ktRenderer,
    std::chrono::high_resolution_clock::time_point frameTime)
{
    if (!m_isStarted)
    {
        return;
    }

    float fElapsedMs = std::chrono::duration<float>(frameTime - m_lastFrameTime).count();
    m_lastFrameTime = frameTime;

    auto env = GetJNIEnv();

    // m_ktAdvanceCallback 在 start 函数中定义, 指向 kotlin 中 Renderer.advance 函数
    JNIExceptionHandler::CallVoidMethod(env, ktRenderer, m_ktAdvanceCallback, fElapsedMs);

    tracer->beginSection("draw()");

    // 准备渲染状态, 调用 eglMakeCurrent 函数, 或 surface.lockCanvas 函数
    prepareForDraw(threadState);
    // m_ktDrawCallback 在 start 函数中定义, 指向 kotlin 中 Renderer.draw 函数
    JNIExceptionHandler::CallVoidMethod(env, ktRenderer, m_ktDrawCallback);

    tracer->beginSection("flush()");
    flush(threadState); // 提交渲染指令
    tracer->endSection(); // flush

    tracer->beginSection("swapBuffers()");
    threadState->swapBuffers(); // 交换缓冲区
    tracer->endSection(); // swapBuffers

    tracer->endSection(); // draw()
}

​ 接下来,分别介绍 prepareForDraw、flush、swapBuffers。

5.4.2 prepareForDraw

1)EGLWorkerImpl::prepareForDraw

​ EGLWorkerImpl 的 prepareForDraw 函数源码如下。PLSThreadState 中实现了 makeCurrent 函数(见 2.6.3 节),eglThreadState->makeCurrent 中 调用了 eglMakeCurrent 函数,eglMakeCurrent 是 EGL 环境创建的最后一步,作用是绑定 EGLSurface 和 EGLContext 到显示设备(EGLDisplay)。

​ models/worker_impl.hpp

cpp 复制代码
virtual void prepareForDraw(DrawableThreadState* threadState) const override
{
	auto eglThreadState = static_cast<EGLThreadState*>(threadState);
	// 里面调用了 eglMakeCurrent, 用于绑定 EGLSurface 和 EGLContext 到显示设备 (EGLDisplay)
	eglThreadState->makeCurrent(m_eglSurface);
	clear(threadState);
}

2)CanvasWorkerImpl::prepareForDraw

​ CanvasWorkerImpl 的 prepareForDraw 函数源码如下。其作用是通过 surface.lockCanvas 方法拿到 Canvas 对象。

​ models/worker_impl.cpp

cpp 复制代码
void CanvasWorkerImpl::prepareForDraw(DrawableThreadState*) const
{
    m_canvasRenderer->bindCanvas(m_ktSurface);
}

​ bindCanvas 函数源码如下,m_ktCanvas 是通过 surface.lockCanvas 函数获取的 Canvas 对象。

​ models/canvas_renderer.hpp

cpp 复制代码
void bindCanvas(jobject ktSurface)
{
	...
	JNIEnv* env = GetJNIEnv();
    // 通过 surface.lockCanvas 函数获取 Canvas 对象
	m_ktCanvas = env->NewGlobalRef(GetCanvas(ktSurface));
    // 通过 canvas.getWidth 函数获取 Canvas 的宽度
	m_width = JNIExceptionHandler::CallIntMethod(env, m_ktCanvas, GetCanvasWidthMethodId());
    // 通过 canvas.getHeight 函数获取 Canvas 的高度
	m_height = JNIExceptionHandler::CallIntMethod(env, m_ktCanvas, GetCanvasHeightMethodId());
	Clear(m_ktCanvas);
}

static jobject GetCanvas(jobject ktSurface)
{
	return GetJNIEnv()->CallObjectMethod(ktSurface, GetSurfaceLockCanvasMethodId(), nullptr);
}

​ GetSurfaceLockCanvasMethodId 用于获取 surface.lockCanvas 的方法 id,具体实现如下。这里没有调用 surface.lockHardwareCanvas 方法,说明 Canvas 渲染方式是软渲染(即 CPU 渲染,而不是 GPU 渲染)。

​ models/jni_refs.cpp

cpp 复制代码
jmethodID GetSurfaceLockCanvasMethodId()
{
    return GetMethodId(GetAndroidSurfaceClass(), "lockCanvas", "(Landroid/graphics/Rect;)Landroid/graphics/Canvas;");
}

jmethodID GetMethodId(jclass clazz, const char* name, const char* sig)
{
    JNIEnv* env = GetJNIEnv();
    jmethodID output = env->GetMethodID(clazz, name, sig);
    env->DeleteLocalRef(clazz);
    return output;
}

jclass GetAndroidSurfaceClass() { return GetClass("android/view/Surface"); }

5.4.3 flush

1)PLSWorkerImpl::flush

​ PLSWorkerImpl 的 flush 函数如下。renderContext->flush 函数的源码不在 rive-android 代码库中,在 rive-runtime 代码库中,详见 → render_context.cpp

​ models/worker_impl.cpp

cpp 复制代码
void PLSWorkerImpl::flush(DrawableThreadState* threadState) const
{
    PLSThreadState* plsThreadState = PLSWorkerImpl::PlsThreadState(threadState);
    rive::gpu::RenderContext* renderContext = plsThreadState->renderContext();
    renderContext->flush({.renderTarget = m_renderTarget.get()});
}

2)CanvasWorkerImpl::flush

​ CanvasWorkerImpl 的 flush 函数如下,最终会调用 surface.unlockCanvasAndPost 函数。

​ models/worker_impl.cpp

cpp 复制代码
void CanvasWorkerImpl::flush(DrawableThreadState*) const
{
    m_canvasRenderer->unlockAndPost(m_ktSurface);
}

​ models/canvas_renderer.hpp

cpp 复制代码
void unlockAndPost(jobject ktSurface)
{
	JNIEnv* env = GetJNIEnv();
	JNIExceptionHandler::CallVoidMethod(env, ktSurface, GetSurfaceUnlockCanvasAndPostMethodId(), m_ktCanvas);
	m_width = -1;
	m_height = -1;
	env->DeleteGlobalRef(m_ktCanvas);
	m_ktCanvas = nullptr;
}

​ GetSurfaceUnlockCanvasAndPostMethodId 函数调用了 surface.unlockCanvasAndPost 函数,具体实现如下。

​ models/jni_refs.cpp

cpp 复制代码
jmethodID GetSurfaceUnlockCanvasAndPostMethodId()
{
    return GetMethodId(GetAndroidSurfaceClass(), "unlockCanvasAndPost", "(Landroid/graphics/Canvas;)V");
}

jmethodID GetMethodId(jclass clazz, const char* name, const char* sig)
{
    JNIEnv* env = GetJNIEnv();
    jmethodID output = env->GetMethodID(clazz, name, sig);
    env->DeleteLocalRef(clazz);
    return output;
}

jclass GetAndroidSurfaceClass() { return GetClass("android/view/Surface"); }

5.4.4 EGLThreadState::swapBuffers

​ DrawableThreadState 的子类中,只有 EGLThreadState 重写了 swapBuffers 函数,如下。eglSwapBuffers 函数用于交换缓冲区。

​ helpers/thread_state_egl.cpp

cpp 复制代码
void EGLThreadState::swapBuffers()
{
    eglSwapBuffers(m_display, m_currentSurface); // 交换缓冲区
    EGL_ERR_CHECK();
}

5.5 RiveArtboardRenderer.draw

​ 5.4.1 节 WorkerImpl::doFrame 函数中,在 prepareForDraw 之后,调用了 m_ktDrawCallback,它指向的是 kotlin 中 Renderer.draw 函数,它是个抽象函数,RiveArtboardRenderer 中实现了该函数,如下。controller 是 RiveFileController 对象,它在 RiveAnimationView 的 init 代码块中创建,在 createRenderer 函数中传递给 RiveArtboardRenderer。activeArtboard 是 Artboard 对象,

​ app.rive.runtime.kotlin.renderers.RiveArtboardRenderer.kt

Kotlin 复制代码
@WorkerThread
override fun draw() {
	synchronized(controller.file?.lock ?: this) {
		...
		controller.activeArtboard?.draw(cppPointer, fit, alignment, scaleFactor = scaleFactor)
	}
}

5.6 Artboard.draw

​ app.rive.runtime.kotlin.core.Artboard.kt

Kotlin 复制代码
@WorkerThread
fun draw(rendererAddress: Long, fit: Fit, alignment: Alignment, scaleFactor: Float = 1.0f) {
	synchronized(lock) {
		cppDrawAligned(cppPointer, rendererAddress, fit, alignment, scaleFactor)
	}
}

5.7 JNI cppDrawAligned

​ 全局搜索 "Artboard_cppDrawAligned",找到 JNI 中的 cppDrawAligned 函数的实现如下。首先获取 rive::ArtboardInstance 对象,接着通过 getRendererOnWorkerThread 函数获取 rive::RiveRenderer 或 CanavsRenderer 对象,最后调用 artboard->draw(renderer) 函数渲染一帧画面。rive::ArtboardInstance 的源码不在 rive-android 代码库中,在 rive-runtime 代码库中,详见 → artboard.hppartboard.cpp

​ bindings/bindings_artboard.cpp

cpp 复制代码
JNIEXPORT void JNICALL
Java_app_rive_runtime_kotlin_core_Artboard_cppDrawAligned(JNIEnv* env, jobject, jlong artboardRef,
	jlong rendererRef, jobject ktFit, jobject ktAlignment, jfloat scaleFactor)
{
	auto artboard = reinterpret_cast<rive::ArtboardInstance*>(artboardRef); // 获取 ArtboardInstance 对象(未开放源码)
	auto jniWrapper = reinterpret_cast<JNIRenderer*>(rendererRef); // 获取 JNIRenderer 对象
    // 获取 rive::RiveRenderer 对象或 CanvasRenderer 对象
	rive::Renderer* renderer = jniWrapper->getRendererOnWorkerThread();

	rive::Fit fit = GetFit(env, ktFit);
	rive::Alignment alignment = GetAlignment(env, ktAlignment);

	renderer->save(); // 如果 renderer 是 CanvasRenderer, 将调用 canvas.save 函数
	renderer->align(fit, alignment,
		rive::AABB(0, 0, jniWrapper->width(), jniWrapper->height()),
		artboard->bounds(),scaleFactor);
	artboard->draw(renderer);
	renderer->restore(); // 如果 renderer 是 CanvasRenderer, 将调用 canvas.restore 函数
}

6 启动渲染流程

​ 启动渲染流程图如下。

6.1 启动渲染的源头

​ 在 Rive 的 kotlin 源码中,通过调用 Renderer.start 函数启动渲染,但是,用户无法直接调用该函数,调用该函数的地方非常多,分成以下几类。

1)直接调用

​ 以下函数中会直接调用 Renderer.start 函数启动渲染。

  • RiveAnimationView.onAttachedToWindow
  • RiveTextureView.onVisibilityChanged
  • Renderer.setSurface

2)RiveFileController.onStart

​ RiveFileController.onStart 也指向了 Renderer.start 函数,在 RiveFileController 的 fit、alignment、layoutScaleFactor、layoutScaleFactorAutomatic 等属性变化、以及 autoplay 属性变为 true 时,会调用 onStart.invoke 启动渲染。

3)间接调用

​ 在 RiveAnimationView 的以下函数中,经过多步调用,最终会调用到 Renderer.start 函数启动渲染。

  • play
  • reset
  • fireState
  • setBooleanState
  • setNumberState
  • setTextRunValue
  • onTouchEvent

6.2 Renderer.start

​ Renderer 的 start 函数如下,通过 Choreographer.getInstance().postFrameCallback 函数让 doFrame 函数每帧调用一次,通过 cppStart 函数启动 Rive 引擎(rive::AudioEngine::RuntimeEngine)。

​ app.rive.runtime.kotlin.renderers.Renderer.kt

Kotlin 复制代码
abstract class Renderer(
    @get:VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    var type: RendererType = Rive.defaultRendererType,
    val trace: Boolean = false
) : NativeObject(NULL_POINTER), Choreographer.FrameCallback

    fun start() {
        if (isPlaying) return
        if (!isAttached) return
        if (!hasCppObject) return
        isPlaying = true
        cppStart(cppPointer)
        scheduleFrame()
    }

	open fun scheduleFrame() {
        Handler(Looper.getMainLooper()).post {
            Choreographer.getInstance().postFrameCallback(this@Renderer)
        }
    }

	@CallSuper
    override fun doFrame(frameTimeNanos: Long) {
        if (isPlaying) {
            cppDoFrame(cppPointer)
            scheduleFrame()
        }
    }
}

6.3 JNI cppStart

​ 全局搜索 "Renderer_cppStart",找到 JNI 中的 cppStart 函数的实现如下。

​ bindings/bindings_renderers.cpp

cpp 复制代码
JNIEXPORT void JNICALL
Java_app_rive_runtime_kotlin_renderers_Renderer_cppStart(JNIEnv*, jobject, jlong rendererRef)
{
	reinterpret_cast<JNIRenderer*>(rendererRef)->start();
}

6.4 JNIRenderer::start

​ JNIRenderer::start 的源码如下。m_worker 是 RefWorker 类的实例,RefWorker 继承 WorkThread,WorkThread 中创建了工作线程(见 3.5 节),并且有个 run 函数,m_worker->run 表示把任务提交到 WorkThread 中的工作线程中执行。m_workerImpl 是 4.6 节创建的 WorkerImpl 对象(可能是 PLSWorkerImpl 或 CanavsWorkerImpl)。

​ models/jni_renderer.cpp

cpp 复制代码
void JNIRenderer::start()
{
    m_worker->run([this](DrawableThreadState* threadState) {
        if (!m_workerImpl)
            return;
        auto now = std::chrono::steady_clock::now();
        m_fpsLastFrameTime = now;
        m_workerImpl->start(m_ktRenderer, now);
    });
}

6.5 WorkerImpl::start

​ WorkerImpl::start 函数源码如下。这里主要初始化 m_ktRendererClass 、m_ktDrawCallback、m_ktAdvanceCallback,并启动 Rive 引擎。

​ models/worker_impl.cpp

cpp 复制代码
void WorkerImpl::start(jobject ktRenderer, std::chrono::high_resolution_clock::time_point frameTime)
{
    auto env = GetJNIEnv();
    jclass ktClass = env->GetObjectClass(ktRenderer);
    m_ktRendererClass = reinterpret_cast<jclass>(env->NewWeakGlobalRef(ktClass)); // 获取 kotlin 中 Renderer 对象
    m_ktDrawCallback = env->GetMethodID(m_ktRendererClass, "draw", "()V"); // 指向 Renderer.draw 方法
    m_ktAdvanceCallback = env->GetMethodID(m_ktRendererClass, "advance", "(F)V");  // 指向 Renderer.advance 方法
    m_lastFrameTime = frameTime;
    m_isStarted = true;
    if (auto engine = rive::AudioEngine::RuntimeEngine(false))
    {
        engine->start(); // 启动 Rive 引擎
    }
}

7 暂停渲染流程

​ 暂停渲染流程图如下。

7.1 暂停渲染的源头

​ 在 Rive 的 kotlin 源码中,通过调用 Renderer.stop 函数暂停渲染,但是,用户无法直接调用该函数,调用该函数的地方非常多,主要有以下几处。

  • RiveAnimationView.pause
  • RiveAnimationView.onDetachedFromWindow
  • RiveTextureView.onVisibilityChanged

7.2 Renderer.stop

​ Renderer 的 stop 函数如下,通过 cppStop 函数暂停 Rive 引擎(rive::AudioEngine::RuntimeEngine),通过 Choreographer.getInstance().removeFrameCallback 函数移除 doFrame 回调。

​ app.rive.runtime.kotlin.renderers.Renderer.kt

Kotlin 复制代码
abstract class Renderer(
    @get:VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    var type: RendererType = Rive.defaultRendererType,
    val trace: Boolean = false
) : NativeObject(NULL_POINTER), Choreographer.FrameCallback

    @CallSuper
    fun stop() {
        stopThread()
        Handler(Looper.getMainLooper()).post { // postFrameCallback must be called from the main looper
            Choreographer.getInstance().removeFrameCallback(this@Renderer)
        }
    }

    @CallSuper
    internal fun stopThread() {
        if (!isPlaying) return
        if (!hasCppObject) return
        isPlaying = false
        cppStop(cppPointer)
    }

	open fun scheduleFrame() {
        Handler(Looper.getMainLooper()).post {
            Choreographer.getInstance().postFrameCallback(this@Renderer)
        }
    }

	@CallSuper
    override fun doFrame(frameTimeNanos: Long) {
        if (isPlaying) {
            cppDoFrame(cppPointer)
            scheduleFrame()
        }
    }
}

7.3 JNI cppStop

​ 全局搜索 "Renderer_cppStop",找到 JNI 中的 cppStop 函数的实现如下。

​ bindings/bindings_renderers.cpp

cpp 复制代码
JNIEXPORT void JNICALL
Java_app_rive_runtime_kotlin_renderers_Renderer_cppStop(JNIEnv*, jobject, jlong rendererRef)
{
	reinterpret_cast<JNIRenderer*>(rendererRef)->stop();
}

7.4 JNIRenderer::stop

​ JNIRenderer::stop 的源码如下。m_worker 是 RefWorker 类的实例,RefWorker 继承 WorkThread,WorkThread 中创建了工作线程(见 3.5 节),并且有个 run 函数,m_worker->run 表示把任务提交到 WorkThread 中的工作线程中执行。m_workerImpl 是 4.6 节创建的 WorkerImpl 对象(可能是 PLSWorkerImpl 或 CanavsWorkerImpl)。

​ models/jni_renderer.cpp

cpp 复制代码
void JNIRenderer::stop()
{
    m_worker->run([this](DrawableThreadState* threadState) {
        if (!m_workerImpl)
            return;
        m_workerImpl->stop();
    });
}

7.5 WorkerImpl::stop

​ WorkerImpl::stop 函数源码如下。这里主要暂停 Rive 引擎,并将 m_ktRendererClass 、m_ktDrawCallback、m_ktAdvanceCallback 设置为空。

​ models/worker_impl.cpp

cpp 复制代码
void WorkerImpl::stop()
{
    if (auto engine = rive::AudioEngine::RuntimeEngine(false))
    {
        engine->stop(); // 暂停 Rive 引擎
    }
    auto env = GetJNIEnv();
    if (m_ktRendererClass != nullptr)
    {
        env->DeleteWeakGlobalRef(m_ktRendererClass);
    }
    m_ktRendererClass = nullptr;
    m_ktDrawCallback = nullptr;
    m_ktAdvanceCallback = nullptr;
    m_isStarted = false;
}

声明:本文转自【Rive】rive-android源码分析

相关推荐
阿巴斯甜11 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker11 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952712 小时前
Andorid Google 登录接入文档
android
黄林晴13 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android