Android手写笔低延迟 - 2

1,186 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天,点击查看活动详情

Android手写笔低延迟

利用低延迟图形和运动预测API创建自然的手写笔体验

续接上篇: Android手写笔低延迟 - 1

前缓冲渲染: 直接到屏幕

为了实现前缓冲技术, 引入了前缓冲层和双缓冲层. 前缓冲层是为快速渲染屏幕上的小部分内容而设计的. 该层是透明和短暂的, 我们一会儿就会看到.

前缓冲层的输入被渲染到双缓冲层以持续工作. 然后前缓冲区被隐藏起来.

但是你可能想知道前面的缓冲区是如何做到比普通的双缓冲区方法渲染得更快的以及有什么注意事项

低延迟图形库直接写到前缓冲区--换句话说, 直接写到屏幕上. 这之所以有效, 是因为屏幕上只有一小块区域被改变(每一帧只有一小部分触笔笔触被修改)

一旦举起手写笔, 就会恢复采用双缓冲技术的常规渲染, 并且笔画会持续存在.

1_N-mtwdASsL5GuBBatIBt1w.gif

动画展示了前缓冲渲染一旦完成是如何复制stroke到双缓冲区的.

什么时候使用低延迟图形库

前缓冲区技术对特定的使用情况效果最好--特别是手写, 绘画和素描. 这些操作只修改了屏幕的一小部分, 这也是该库的闪光点. 如果我们要修改更大的区域, 这可能会产生撕裂和人工痕迹.

低延迟图形库不是用来渲染整个屏幕的, 比如说游戏, 或者当大面积修改时, 比如平移或缩放. 安卓有其他工具,比如安卓游戏开发, 以及常规的OpenGL渲染, 可以在这些情况下帮助开发者.

低延迟图形库可用于安卓10(API级别29)及更高版本和运行安卓11及更高版本的ChromeOS 设备.

在我们看到代码的样子之前, 你可能要考虑一下你的应用程序, 评估一下哪些工作应该渲染到前缓冲区(影响屏幕一小部分的短工作, 比如一笔), 哪些工作应该在双缓冲层中处理(更大的区域,比如平移和缩放).

1_WYFvZQ2nDQ7-eqhyZMZ8hw.webp

做与不做 - 将低延迟用于小屏幕区域的更新, 不要用于全屏渲染, 如缩放, 平移, 游戏等.

实现

让我们开始编码吧! GLFrontBufferedRenderer与你自己的数据类型一起工作, 它通常包含x和y坐标, 但也包含颜色, 笔刷类型, 压力等等.

该方法需要两个回调, 其中OpenGL代码将被执行, 一个用于在前缓冲区(笔画的delta)渲染, 使用onDrawFrontBufferedLayer, 另一个用于双缓冲区(持久性), 使用onDrawDoubleBufferedLayer.

/* Copyright 2022 Google LLC.	
   SPDX-License-Identifier: Apache-2.0 */
   
val callbacks = object : GLFrontBufferedRenderer.Callback<DATA_TYPE> {
    override fun onDrawFrontBufferedLayer(
        eglManager: EGLManager,
        bufferWidth: Int,
        bufferHeight: Int,
        transform: FloatArray,
        param: DATA_TYPE
    ) {
        // OpenGL work for Front buffer layer
    }

    override fun onDrawDoubleBufferedLayer(
        eglManager: EGLManager,
        bufferWidth: Int,
        bufferHeight: Int,
        transform: FloatArray,
        params: Collection<DATA_TYPE>
    ) {
        // OpenGL work for Doubled buffer layer
    }
}

val frontBufferRenderer = GLFrontBufferedRenderer(mySurfaceView, callbacks)

// Add a OnTouchListener, to get the motion data and send then  
// to frontBufferRenderer for rendering.
mySurfaceView.setOnTouchListener { v, event ->
            // MotionEvent to DATA_TYPE - function to implement yourself
            val dataPoint = event.toDATA_TYPE()
                
            when (event.action) {
                MotionEvent.ACTION_DOWN -> {
                    // Render a new data point directly to the front buffer layer
                    frontBufferRenderer.renderFrontBufferedLayer(dataPoint)
                }

                MotionEvent.ACTION_MOVE -> {
                    // Render a new data point directly to the front buffer layer
                    frontBufferRenderer.renderFrontBufferedLayer(dataPoint)
                }

                MotionEvent.ACTION_UP -> {
                    // Stroke is done, commit the data points to the double buffered layer
                    frontBufferRenderer.commit()
                }
                else -> Unit
            }
            true
        }

推荐Nader Jawad的Android应用手写笔演讲, 以及2022年Android开发者峰会上发布的Android Graphics演讲.

同时还可以参考低延迟图形库文档.

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天,点击查看活动详情