Android shader编译原理

文章讨论了着色器编译导致的帧率下降问题,特别是在Flutter和Android环境中。Flutter通过SkSL预热来缓解这个问题,而Android系统则使用ShaderCache存储编译后的着色器,减少运行时编译的开销。文章还探讨了Android中Shader的编译、链接流程以及预加载技术,以提高性能和用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

作者:tmaczhang

1. 什么是着色器编译卡顿?

着色器是在 GPU(图形处理单元)上运行的代码。当 Flutter 渲染的 Skia 图形后端首次看到新的绘制命令序列时,它有时会生成和编译一个自定义的 GPU 着色器用于该命令序列。使得该序列和潜在类似的序列能够尽可能快地渲染。

然而不幸的是,Skia 着色器生成和编译的过程与帧的工作是依次进行的。编译过程可能需要几百毫秒的时间,而对于 60 帧/秒 (frame-per-second) 的显示来说,一个流畅的帧必须在 16 毫秒内绘制完成。因此,编译过程可能导致数十帧被丢失,使帧数从 60 降到 6。这就是所谓的 编译卡顿 。编译完成之后,动画应该会变得流畅。

另一方面,Impeller 在我们构建 Flutter 引擎时已经生成并编译了所有必要的着色器。因此,在 Impeller 上运行的应用程序已经拥有了它们所需的所有着色器,并且这些着色器不会在动画中引起卡顿。

要获得更加确切的着色器编译卡顿存在的证据,你可以在 --trace-skia 开启时查看追踪文件中的 GrGLProgramBuilder::finalize。下面的截图展示了一个 timeline 追踪的样例。

如何使用 SkSL 预热

在 1.20 发布的时候,Flutter 为应用开发者提供了一个命令行工具以收集终端用户在 SkSL(Skia 着色器语言)进行格式化处理中需要用到的着色器。 SkSL 着色器可以被打包进应用,并提前进行预热(预编译),这样当终端用户第一次打开应用时,就能够减少动画的编译掉帧了。

在flutter中,通过将SKSL着色器打包进应用,并且提前进行预编译,用空间换取时间,来提升性能,那么在android里是不是同样可以呢?

2 Android中的Shader编译和使用

2.1 shader原始逻辑

/data/user_de/0/tv.danmaku.bili/code_cache # ls -l
total 56
-r-------- 1 u0_a206 u0_a206_cache 40556 2023-06-30 15:32 com.android.opengl.shaders_cache
-r-------- 1 u0_a206 u0_a206_cache 13304 2023-06-30 15:32 com.android.skia.shaders_cache

frameworks/base/graphics/java/android/graphics/HardwareRenderer.java

/**
 * Name of the file that holds the shaders cache.
 */
private static final String CACHE_PATH_SHADERS = "com.android.opengl.shaders_cache";
private static final String CACHE_PATH_SKIASHADERS = "com.android.skia.shaders_cache";



/**
 * Sets the directory to use as a persistent storage for threaded rendering
 * resources.
 *
 * @param cacheDir A directory the current process can write to
 * @hide
 */
public static void setupDiskCache(File cacheDir) {
    setupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath(),
            new File(cacheDir, CACHE_PATH_SKIASHADERS).getAbsolutePath());
}

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);
}

2.2 Skia介绍

在Render线程初始化的时候,会初始化路径,并且设置到native里。那么是怎么保存的呢?这就要介绍今天的主角SKia库。

android路径位于 external/skia/

官方描述:SkSL是Skia的着色语言。SkRuntimeEffect是一个Skia C++对象,可用于创建行为由SkSL代码控制的SkShader、SkColorFilter和SkBlender对象。 您可以在上试用SkSLhttps://shaders.skia.org/.语法与GLSL非常相似。在您的滑雪应用程序中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值