Android显示学习笔记本

根据博客 Android-View 绘制原理(01)-JAVA层分析_android view draw原理分析-CSDN博客

提出了我的疑问  Canvas RenderNode updateDisplayListDirty 这些东西的关系

您的理解在基本方向上是对的,但让我详细解释一下 Android 中 updateDisplayListDirty、指令集合、Canvas 和 RenderNode 之间的关系:

RenderNode 与渲染过程

在 Android 的渲染系统中:

  1. RenderNode
    • 是一个存储渲染操作的容器
    • 包含了视图的绘制命令和属性信息
    • 可以被缓存和重用,提高渲染效率
  2. DisplayList
    • 实际上是 RenderNode 内部的绘制命令列表
    • 存储了一系列需要执行的渲染指令
    • 通过 updateDisplayListDirty 方法更新这些指令
  3. Canvas
    • 是应用层与渲染系统交互的接口
    • 提供了各种绘制方法(drawRect、drawText 等)
    • 当我们调用 Canvas 的绘制方法时,它会转换为渲染指令

updateDisplayListDirty 与 Canvas 的关系

updateDisplayListDirty 方法在 View 类中实现,主要作用是:

  1. 创建或获取一个 RenderNode
  2. 使用 DisplayListCanvas(特殊的 Canvas 实现)录制绘制指令
  3. 当视图调用 onDraw() 方法时,它使用提供的 Canvas 执行绘制
  4. 这些绘制命令被记录到 DisplayList 中
  5. 最终生成或更新 RenderNode

Canvas 和 DisplayList 不是可替代关系,而是协作关系:

  • Canvas 是绘制接口,应用通过它表达"画什么"
  • DisplayList 是指令集合,记录"如何画"
  • RenderNode 是整个结构的容器,包含了 DisplayList 和其他渲染属性

所以不是"有一个就可以了",而是它们在渲染管线中扮演不同角色:Canvas 是应用用来绘制的工具,而 DisplayList 是记录这些绘制指令的容器,两者共同构成了 Android 的高效渲染系统。

 看完后我的疑问变成了,既然 updateDisplayListDirty  是一个指令集合 在硬件加速的情况下,一个渲染当VSYNC信号来了,进行三大流程进行获取surfaceControl然后进行 绘制,绘制时候先新建RenderNode 然后获取 RenderNode 的 

RecordingCanvas canvas = mRootNode.beginRecording(mSurfaceWidth, mSurfaceHeight);
RecordingCanvas 然后把它变成后续的 Canvas
最后  

   public void endRecording() {if (mCurrentRecordingCanvas == null) {throw new IllegalStateException("No recording in progress, forgot to call #beginRecording()?");}RecordingCanvas canvas = mCurrentRecordingCanvas;mCurrentRecordingCanvas = null;canvas.finishRecording(this);canvas.recycle();}

finishRecording 把 当前的 RenderNode 传过去 然后进行绘制

那么我的问题是 之后surfaceFlinger是否绘制的是指令集合?是否是当RecordingCanvas  执行一次生成一个指令就绘制一部分还是 finishRecording 后一起进行绘制?

您的问题涉及到 Android 渲染流水线的关键细节,我来详细解释:

finishRecording 与渲染流程

在 Android 的硬件加速渲染流程中,当 finishRecording() 被调用时:

  1. 指令集完成阶段
    • finishRecording() 标志着 RecordingCanvas 已经完成所有绘制命令的记录
    • 此时,所有来自 onDraw() 的绘制操作已经被转换为渲染指令并存储在 RenderNode 中
    • 这些指令并不会立即触发绘制
  2. 批处理渲染模式
    • SurfaceFlinger 不会 在每条指令生成后立即渲染
    • 系统采用批处理模式,收集完所有指令后一次性处理
    • 这种方式大大提高了渲染效率,避免了频繁的状态切换和上下文切换
  3. 渲染时机
    • ViewRootImpl.performTraversals() 完成后,会调用 ThreadedRenderer.draw()
    • 此时,所有视图的 RenderNode 都已准备就绪
    • 系统会将这些渲染命令通过 HWUI 传递给 GPU
    • SurfaceFlinger 会在下一个 VSYNC 信号到来时合成并显示结果
  4. SurfaceFlinger 处理
    • SurfaceFlinger 接收的是已经处理过的图层数据
    • 它主要负责合成多个应用的图层,而不是执行具体的绘制指令
    • 实际的指令执行发生在 GPU 上,在应用进程的渲染线程中

所以,回答您的核心问题:系统是在 finishRecording() 后,将完整的指令集合一次性提交给渲染管线进行处理,而不是每生成一条指令就执行一次渲染。这种批处理模式是现代图形渲染系统的普遍做法,可以最大限度地减少 GPU 状态切换,提高渲染效率。

需要注意的是,RecordingCanvas 记录的指令集和 SurfaceFlinger 处理的内容在概念上有区别——前者是应用内的绘制指令,后者处理的是已经渲染好的图层。

 

那这些指令在哪被处理?

这些渲染指令的处理发生在多个层次的渲染管线中,让我详细解释这个流程:

渲染指令的处理路径

  1. 应用进程中的处理
    • finishRecording() 完成后,渲染指令首先在应用自己的渲染线程中处理
    • ThreadedRenderer 类负责协调这个过程
    • 具体处理由 HWUI (Hardware User Interface) 库在原生层完成
    • 在这一阶段,渲染指令被转换为 GPU 可以理解的命令(如 OpenGL ES 或 Vulkan 调用)
  2. RenderThread 中的执行
    • Android 使用专门的 RenderThread(渲染线程)处理这些指令
    • 这个线程与主线程分离,可以并行处理渲染工作
    • 渲染指令在这里被组织成命令缓冲区(Command Buffer)
    • 这些命令最终会通过图形驱动提交给 GPU
  3. GPU 执行阶段
    • GPU 接收这些渲染命令并执行实际的像素处理
    • 渲染结果被写入到应用的 Surface 对应的图形缓冲区中
    • 这个过程完全由 GPU 硬件加速完成
  4. SurfaceFlinger 的角色
    • SurfaceFlinger 不处理原始渲染指令,而是处理已渲染好的图层
    • 它从各个应用获取已经渲染好的图形缓冲区
    • 然后进行合成(Composition)操作,决定不同应用的图层如何叠加显示
    • 最后将合成结果提交给显示硬件

具体源码路径

如果您想在源码中追踪这个处理过程:

  1. ThreadedRenderer.nSyncAndDrawFrame() 是关键的入口点,它通过 JNI 调用原生层代码
  2. 在原生层,android_view_ThreadedRenderer.cpp 包含了相关实现
  3. 进一步深入到 libs/hwui/ 目录,特别是 RenderProxyRenderThread 相关类
  4. 最终的 GPU 命令提交通过图形驱动 API(如 OpenGL ES 或 Vulkan)完成

所以,渲染指令主要在应用自己的渲染线程中被处理和执行,而不是在 SurfaceFlinger 中。SurfaceFlinger 的工作是在所有应用都完成各自的渲染后,组合这些结果并显示到屏幕上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/78548.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaWeb学习打卡-Day4-会话技术、JWT、Filter、Interceptor

会话技术 会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应。会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求…

让数据优雅落地:用 serde::Deserialize 玩转结构体实体

前言 想象一下,服务器突然飞来一堆 JSON 数据,就像一群无头苍蝇冲进办公室,嗡嗡作响,横冲直撞。此刻,你的任务,就是把这群“迷路数据”安置进正确的格子里,分门别类,秩序井然,不混不乱,不漏一只。 好在 Rust 早就为我们备好瑞士军刀:serde::Deserialize。它不仅刀…

Virtio 技术解析 | 框架、设备实现与实践指南

本文为 “Virtio” 相关文章合辑。 略作重排,如有内容异常,请看原文。 Virtio 简介(一)—— 框架分析 posted 2021-04-21 10:14 Edver 1. 概述 在传统设备模拟中,虚拟机内部设备驱动完全不知自身处于虚拟化环境&a…

云计算赋能质检LIMS的价值 质检LIMS系统在云计算企业的创新应用

在云计算技术高速发展的背景下,实验室信息化管理正经历深刻变革。质检LIMS(实验室信息管理系统)作为实验室数字化转型的核心工具,通过与云计算深度融合,为企业提供了高弹性、高安全性的解决方案。本文将探讨质检LIMS在…

【win11 安装WSL2 详解一遍过!!】

共有五个步骤,按部就班的做,保准成功! 1. 打开开发者模式 设置->系统->开发者模式 2. 打开linux的win子系统 找到控制面板-程序和功能-启用或关闭Windows功能,选中“适用于Linux的Windows子系统”,“虚拟机…

Godot开发2D冒险游戏——第三节:游戏地图绘制

一、初步构建游戏地图 在游戏场景当中添加一个新的子节点:TileMapLayer 这一层称为瓦片地图层 根据提示,下一步显然是添加资源 为TileMapLayer节点添加一个TileSet 将地板添加进来,然后选择自动分割图集 自定义时要确保大小合适 让Godot自…

Django创建的应用目录详细解释以及如何操作数据库自动创建表

创建好Django项目后 如果要创建 python manage.py startapp 模块名模块 使用 我创建一个system模块后是 注意:urls是我自己建的文件 1.migrations目录 存放数据库的迁移文件,当models.py中模型定义发生变化时,通过迁移操作能同步数据库结构变化 __init__ 使该目录…

将输入帧上下文打包到下一个帧的预测模型中用于视频生成

Paper Title: Packing Input Frame Context in Next-Frame Prediction Models for Video Generation 论文发布于2025年4月17日 Abstract部分 在这篇论文中,FramePack是一种新提出的网络结构,旨在解决视频生成中的两个主要问题:遗忘和漂移。 具体来说,遗忘指的是在生成视…

STM32 串口USART

目录 常见的通信方式 串行通信和并行通信 全双工,半双工和单工通信 同步通信和异步通信 通信速率 常见的通信协议 串口基础知识 电平特性 串口传输协议 STM32F103的USART资源 端口引脚 数据寄存器单元 发送接收控制单元 实现串口发送 printf…

Taro on Harmony :助力业务高效开发纯血鸿蒙应用

背景 纯血鸿蒙逐渐成为全球第三大操作系统,业界也掀起了适配鸿蒙原生的浪潮,用户迁移趋势明显,京东作为国民应用,为鸿蒙用户提供完整的购物体验至关重要。   去年 9 月,京东 AP…

gem5-gpu教程05 内存建模

memory-modeling|Details on how memory is modeled in gem5-gpu gem5-gpu’s Memory Simulation gem5-gpu在很大程度上避开了GPGPU-Sim的单独功能模拟,而是使用了gem5的执行中执行模型。因此,当执行存储/加载时,内存会被更新/读取。没有单独的功能路径。(顺便说一句,这…

【python】lambda用法(结合例子理解)

目录 lambda 是什么? 为什么叫 lambda? 语法 举例 1. 最简单的 lambda:单个数字处理 2. 用 lambda 排序一组字符串(按照长度排序) 3. 在列表里找出绝对值最小的数字 4. 给 map() 用 lambda 5. 组合使用:筛选出偶数 lambda 和 def 的对比 lambda 适合用在什么地…

【ROS2】机器人操作系统安装到Ubuntu22.04简介(手动)

主要参考: https://book.guyuehome.com/ROS2/1.系统架构/1.3_ROS2安装方法/ 官方文档:https://docs.ros.org/en/humble/Installation.html 虚拟机与ubuntu系统安装 略,见参考文档 ubutun换国内源,略 1. 设置本地语言 确保您有…

C 调用 C++:extern “C” 接口详解与实践 C/C++混合编译

C 调用 C:extern “C” 接口详解与实践 核心问题在于 C 编译器会对函数名进行“修饰”(Name Mangling)以支持函数重载等特性,而 C 编译器则不会。此外,C 语言本身没有类、对象等概念。为了解决这个问题,我…

汽车制造行业如何在数字化转型中抓住机遇?

近年来,随着新一轮科技革命和产业变革的深入推进,汽车制造行业正迎来一场前所未有的数字化转型浪潮。无论是传统车企还是新势力品牌,都在积极探索如何通过数字化技术提升竞争力、开拓新市场。那么,在这场变革中,汽车制…

k8s学习记录(五):Pod亲和性详解

一、前言 上一篇文章初步探讨了 Kubernetes 的节点亲和性,了解到它在 Pod 调度上比传统方式更灵活高效。今天我们继续讨论亲和性同时Kubernetes 的调度机制。 二、Pod亲和性 上一篇文章中我们介绍了节点亲和性,今天我们讲解一下Pod亲和性。首先我们先看…

HarmonyOS:Navigation实现导航之页面设置和路由操作

导读 设置标题栏模式设置菜单栏设置工具栏路由操作页面跳转页面返回页面替换页面删除移动页面参数获取路由拦截 子页面页面显示类型页面生命周期页面监听和查询 页面转场关闭转场自定义转场共享元素转场 跨包动态路由系统路由表自定义路由表 示例代码 Navigation组件适用于模块…

雪花算法

目录 一、什么是雪花算法 二、使用雪花算法 ​三、使用UUID 使用自增主键是数据库中常用的唯一标识,今天尝试使用mybatisplus来实现三种方式的主键ID 使用起来也很简单 用注解指定一下使用那种方式的主键 一、什么是雪花算法 一种特殊的算法可以计算得到一个唯…

HarmonyOs @hadss/hmrouter路由接入

参考文档:官方文档 在根目录oh-package.json5配置 {"dependencies": {"hadss/hmrouter": "^1.0.0-rc.11"} }加入路由编译插件 hvigor/hvigor-config.json文件 {"dependencies": {"hadss/hmrouter-plugin": &…

C++学习笔记(三十八)——STL之修改算法

STL 算法分类: 类别常见算法作用排序sort、stable_sort、partial_sort、nth_element等排序搜索find、find_if、count、count_if、binary_search等查找元素修改copy、replace、replace_if、swap、fill等修改容器内容删除remove、remove_if、unique等删除元素归约for…