AssetBundle中的shader变体丢失问题

我们通过assetbundle加载材质球或shader时常遇到一个问题:在电脑上测试ok的shader,在手机上显示一片粉红。
出现这种情况的原因有很多,但是其中可能性最大的就是shader变体丢失导致的。

ab资源加载中的shader是如何被引用到的?

加载ab资源中的材质时,会主动索引并加载使用的shader。
“被包含在构建包体中”,指的这几种情况:

  1. shader文件在Resources文件夹下;
  2. shader文件被设置在Setting => Graphics => Always Included Shaders中;
  3. shader被构建包含的场景中使用;这几种情况下shader资源会被打进apk包中。

基于shader是否在构建包体中,有以下2种情况:

  • 如果shader被包含在构建包体内,则可以直接加载使用。
  • 如果shader未被包含在构建包体内,需要通过ab资源加载使用时,使用前shader需要首先加载shader所在的ab资源。 如果ab资源尚未加载,此时使用shader的材质就会出问题。

为什么ab中的shader变体会丢失?

multi_compile和shader_feature是着色器中定义宏的关键字,不同的关键字会生成不同的变体。
相比于multi_compile指令,使用 shader_feature 指令定义的着色器在构建时,没有在材质球中使用到的变体将不会包含在最终的构建中。
所以 shader_feature 用于材质中设置的关键字,而 multi_compile 更适合通过代码来全局设置的关键字。

在Assetbundle打包材质与其引用的shader时:

  • 如果shader和材质在同一个ab包中,在打包时会自动检索到使用的keyword,并将其对应的变体编译打包入ab包中。
  • 如果shader和使用到的材质不在同一个ab包中,则打包时无法检索到使用到了哪些keyword,所以材质使用的变体可能丢失。
    这就是ab打包过程中的变体丢失的问题。

如何避免shader变体丢失?

第一种方法:将shader加入到ProjectSetting=>Graphics=>Always Included Shaders 中。
加在这里的shader不会剔除变体,所以适用于变体少且全部使用到的shader。
如果加入有很多变体的shader,会导致打包包体膨胀。
注意:shader_feature定义的关键字如果未使用,即使加入到了这里,变体也不会保留!

第二种方法:使用ShaderVirantCollection。
我们可以使用ShaderVirantCollection文件来定义使用到的变体,这样打ab包时会自动将定义的变体编译并打包,这样就避免了变体丢失。
注意:ShaderVirantCollection也必须入包(与shader放入同一个ab包),否则不生效。

可以通过shader control插件来搜集使用到的shader变体,从而避免人工查找容易漏掉了某些变体。
当然shader control可以仅可以查询到项目中材质球使用到的shader关键字,如果是通过代码加载/设置shader keyword,则无法通过shader control查询到。

assetbundle打包shader的最佳方案

下面是我当前项目中assetbundle打包shader的方案,暂时定义为我自己的最佳方案:

  • 把assetbundle资源中引用到的shader都放到一个ab包中,在进入游戏时首先加载此ab包,这样可以保证后续所有使用的材质没有问题。
  • 常用到且变体数量少的shader,可以将shader加入到Always Included Shaders 中,然后在打包ab资源时剔除这些shader。
  • 需要assetbundle打包的shader,使用ShaderVirantCollection定义需要打包的变体。可以通过shader control插件来搜集材质球使用到的shader变体,结合人工查找代码中调用使用到的变体。

multip_compile 和shader_feature的区别

相同点

  • 声明Keyword,用来产生Shader的变体(Variant)
#pragma multi_compile A B
//OR #pragma shader_feature A B
  • 这个Shader会被编译成两个变体:一是只包含A模块代码的变体A;二是只包含B模块代码的变体B;
  • 指定的第一个关键字是默认生效的,即默认使用变体A;
  • 在脚本里用Material.EnableKeyword或Shader.EnableKeyword来控制运行时具体使用变体A还是变体B;
  • 它们声明的Keyword是全局的,可以对全局的包含该Keyword的不同Shader起作用;
  • 全局最多只能声明256个(2020.3以后的全局keyword的数量限制是384)这样的Keyword;
  • 请注意Keyword的数量和变体的数量之间的关系,并可能由此导致的性能开销,比如声明#pragma multi_compile A B和#pragma multi_compile D E 这样的两组Keyword会产生 2x2=4 个Shader变体,但若声明10组这样的keyword,则该Shader会产生1024个变体;

不同点
如果使用shader_feature,build时没有用到的变体会被删除,不会打出来。也就是说,在build以后环境里,运行代码Material.EnableKeyword("B")可能不起作用,因为没有Material在使用变体B,所以变体B没有被build出来,运行时也找不到变体B。
如果想解决这个问题,可以采取以下办法中的其中一种:

  1. 使用multi_complie 代替 shader_feature,multi_complie 会把所有变体build出来;
  2. 把这个Shader加入“always included shaders”中 (Project Settings -> Graphic);
  3. 创造一个使用变体B的Material,强行说明变体B有用;

局部keword

全局的Keyword只能有256个,这或许会最终对我们造成限制,而且大部分Keyword并不需要进行全局声明。
因此,我们可以使用multi_complie_local来声明局部的、只在该Shader内部起作用的Keyword,用法相似:

#pragma multi_compile_local __ A
//OR #pragma shader_feature_local __ A

注意:

  • local Keyword仍有数量限制,每个Shader最多可以包含64个local Keyword;
  • 因为这种Keyword是局部的,Material.EnableKeyword仍是有效的,但对Shader.EnableKeyword或CommandBuffer.EnableShaderKeyword这种全局开关来说无法使用;
  • 当你既声明了一个全局的Keyword A ,同时又声明了一个同名的、局部的Keyword A,那么优先认为Keyword A是局部的。

参考链接

Unity Manual - 着色器变体和关键字
Unity Blog - Stripping scriptable shader variants
【Unity游戏开发】马三的游戏性能优化自留地

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容