Example #1
0
        public static ShaderVariantInfo LoadShaderVariantInfo(Shader shader, Dictionary <String, ShaderVariantInfo> shaderVariantCollections = null, bool withLog = true)
        {
            var shaderPath = AssetDatabase.GetAssetPath(shader);

            if (String.IsNullOrEmpty(shaderPath) || EditorUtils.IsUnityDefaultResource(shaderPath))
            {
                return(null);
            }
            ShaderVariantInfo svcInfo = null;

            if (shaderVariantCollections == null || !shaderVariantCollections.TryGetValue(shaderPath, out svcInfo))
            {
                var shaderVariantsPath = System.IO.Path.ChangeExtension(shaderPath, ".shadervariants");
                shaderVariantsPath = shaderVariantsPath.Insert(0, "Assets/ShaderVariants/");
                var svc = AssetDatabase.LoadAssetAtPath <ShaderVariantCollection>(shaderVariantsPath);
                if (svc != null)
                {
                    var comb = ShaderUtils.ParseShaderCombinations(shader, true, withLog: withLog);
                    if (comb != null && comb.snippets != null && comb.snippets.Count > 0)
                    {
                        svcInfo = new ShaderVariantInfo();
                        svcInfo.variantCollection    = svc;
                        svcInfo.rawVariantCollection = ShaderVariantCollectionHelper.ExtractData(svc);
                        svcInfo.combinations         = comb;
                        svcInfo.MergeAllShaderKeywords();
                        if (shaderVariantCollections != null)
                        {
                            shaderVariantCollections.Add(shaderPath, svcInfo);
                        }
                    }
                }
            }
            return(svcInfo);
        }
Example #2
0
 /// <summary>
 /// 一批AssetBundle完成构建,可以认为包中的Shader变体已经被构建完毕,可以做一些校验工作之类
 /// </summary>
 /// <param name="outpath"></param>
 /// <param name="builds"></param>
 /// <param name="manifest"></param>
 internal static void OnEndBuildAssetBundles(String outpath, AssetBundleBuild[] builds, AssetBundleManifest manifest)
 {
     CheckLastShaderVariantCompleteness();
     s_ShaderVariantCollections.Clear();
     lastProcessShader            = null;
     lastProcessShaderVariantInfo = null;
     backupShaderCompilerData.Clear();
 }
Example #3
0
        private void ExecShaderInfo(FrameDebugDumpInfo.ShaderInfo shaderInfo)
        {
            if (string.IsNullOrEmpty(shaderInfo.shaderName))
            {
                return;
            }
            ShaderVariantInfo info;

            if (!variantDict.TryGetValue(shaderInfo.shaderName, out info))
            {
                info = new ShaderVariantInfo(shaderInfo.shaderName);
                variantDict.Add(shaderInfo.shaderName, info);
            }
            info.AddKeywords(shaderInfo.shaderKeywords, shaderInfo.passLightMode);
        }
Example #4
0
        public void OnProcessShader(Shader shader, ShaderSnippetData snippet, IList <ShaderCompilerData> data)
        {
            if (snippet.passType == PassType.ForwardAdd ||
                snippet.passType == PassType.LightPrePassBase ||
                snippet.passType == PassType.LightPrePassFinal ||
                snippet.passType == PassType.Deferred ||
                snippet.passType == PassType.ScriptableRenderPipeline ||
                snippet.passType == PassType.ScriptableRenderPipelineDefaultUnlit ||
                snippet.passType == PassType.Meta ||
                snippet.passType == PassType.MotionVectors)
            {
                data.Clear();
                return;
            }
            var shaderPath = AssetDatabase.GetAssetPath(shader);

            if (EditorUtils.IsUnityDefaultResource(shaderPath))
            {
                return;
            }
            var checkPass  = false;
            var shaderData = ShaderUtil.GetShaderData(shader);

            for (int k = 0; k < shaderData.SubshaderCount; ++k)
            {
                var subShader = shaderData.GetSubshader(k);
                for (int i = 0; i < subShader.PassCount; ++i)
                {
                    var pass = subShader.GetPass(i);
                    if (String.Equals(pass.Name, snippet.passName))
                    {
                        checkPass = true;
                        goto END_PASSCHECK;
                    }
                }
            }
END_PASSCHECK:
            if (!checkPass)
            {
                // snippet 指的是一段可以被编译Shader代码
                // 传入的shader必然含有可编译代码,如果在当前Shader中未找到对应pass,跳过...
                Debug.AssertFormat(false, String.Format("snippet '{0}' is invalid for shader: {1}", snippet.passName, shader.name));
                return;
            }
            // Unity在编译Shader时,会编译出有效三倍变体量,在AB中,代码相同的可能Unity实现了复用机制来减少内存占用
            // 自带三档硬件等级适配:hw_tier00, hw_tier02, hw_tier01
            // 所以,我们收集到的变体个数 * 3 * pow( 2, multi_compile_num )
            // 在编译一个Shader时,会分类型分别多次调用编译管道,比如VS, PS, GS等等
            // 一个pass含有多个snippet

            // 我们收集到的变体只知道归属的PassType
            backupShaderCompilerData.Clear();
            backupShaderCompilerData.AddRange(data);
            var svcInfo = LoadShaderVariantInfo(shader, s_ShaderVariantCollections);

            if (svcInfo != null)
            {
                List <ShaderVariantCollectionHelper.ShaderVariant> rawVariants;
                if (!svcInfo.rawVariantCollection.TryGetValue(shader, out rawVariants))
                {
                    return;
                }
                if (lastProcessShader != shader)
                {
                    CheckLastShaderVariantCompleteness();
                    lastProcessShader            = shader;
                    lastProcessShaderVariantInfo = svcInfo;
                }
                var           removeCount      = 0;
                StringBuilder infoSB           = null;
                var           full_keywords    = new List <String>();
                var           feature_keywords = new HashSet <String>();

                // 通常这里拿到的是自定义multi_compiles,而builtin不在此列
                // 我们在Shader中通过编译器开关: multi_compile_fwdbase来引入builtin变体编译
                // 从字面上看multi_compile_fwdbase是属于multi_compiles,但实际上Unity有专门的优化
                // 从ShaderCompilerData中拿到的每一个ShaderKeyword是可以拿到ShaderKeywordType的
                // 有了精准的Keyword类型,实际上需要对Builtin类的Keywords再次进行归类:
                // 举一个简单例子: DIRECTIONAL LIGHTPROBE_SH VERTEXLIGHT_ON 这些是属于BuiltinDefault
                // 有些高级渲染效果属于BuiltinAutoStripped,这种是被Unity自动优化的keyword类型
                // 我认为可以对常用的Builtin Keywords可以也规划到multi_compiles中,这样可能会引入更多的变体
                // 但是光照效果适配会更多更安全,不会太激进

                // 在这里我们没做任何针对Builtin类型的Keyword做过多安全性保留,所以编译裁剪非常激进,安全性较低
                var multi_compiles = svcInfo.multi_compiles;

                // multi_compile_fwdbase:
                // DIRECTIONAL
                // DIRLIGHTMAP_COMBINED
                // DYNAMICLIGHTMAP_ON
                // LIGHTMAP_ON
                // LIGHTMAP_SHADOW_MIXING
                // LIGHTPROBE_SH
                // SHADOWS_SCREEN
                // SHADOWS_SHADOWMASK
                // VERTEXLIGHT_ON

                backupShaderCompilerData.RemoveAll(
                    _data => {
                    feature_keywords.Clear();
                    full_keywords.Clear();
                    var _keywords = _data.shaderKeywordSet.GetShaderKeywords();
                    // 只剔除有关键字的情形,减少代码复杂度
                    // 实际上,无关键字的变体也可能被丢弃不用,简单舍弃这次剔除操作并不会增加太多编译负担
                    if (_keywords.Length > 0)
                    {
                        for (int j = 0; j < _keywords.Length; ++j)
                        {
                            var name = _keywords[j].GetKeywordName();
                            full_keywords.Add(name);
                            // 如果有当前关键词属于multi_compiles,注意排除
                            if (multi_compiles != null && multi_compiles.Contains(name))
                            {
                                // 排除multi_compiles编译宏,这些是必须使用的,不能剔除
                                continue;
                            }
                            feature_keywords.Add(name);
                        }
                        if (feature_keywords.Count == 0)
                        {
                            return(false);
                        }
                        var matched = false;
                        // 遍历所有从项目中搜集到的变体
                        for (int n = 0; n < rawVariants.Count; ++n)
                        {
                            var variant = rawVariants[n];
                            if (feature_keywords.Count > variant.keywords.Length)
                            {
                                // feature_keywords 已经去掉了multi_compiles,而variant.keywords可能含有
                                // 从数量的差异就能提前能知道不可能匹配得上
                                continue;
                            }
                            var matchCount    = -1;
                            var mismatchCount = 0;
                            var skipCount     = 0;
                            if (variant.shader == shader && variant.passType == snippet.passType)
                            {
                                matchCount = 0;
                                for (var m = 0; m < variant.keywords.Length; ++m)
                                {
                                    var keyword = variant.keywords[m];
                                    if (multi_compiles == null || !multi_compiles.Contains(keyword))
                                    {
                                        // 不是multi_compiles的keyword
                                        if (feature_keywords.Contains(keyword))
                                        {
                                            ++matchCount;
                                        }
                                        else
                                        {
                                            ++mismatchCount;
                                            break;
                                        }
                                    }
                                    else
                                    {
                                        // 查找匹配的变体时,需要排除multi_compiles关键字
                                        ++skipCount;
                                    }
                                }
                            }
                            if (matchCount >= 0 && mismatchCount == 0 &&
                                matchCount == feature_keywords.Count &&
                                matchCount + skipCount == variant.keywords.Length)
                            {
                                matched = true;
                                break;
                            }
                        }
                        if (!matched)
                        {
                            ++removeCount;
                            // 把删除的先存这里,出错了回重新拿回来
                            data.Add(_data);
                            infoSB            = infoSB ?? new StringBuilder();
                            var _fullKeywords = String.Join(" ", full_keywords.ToArray());
                            infoSB.Append(_fullKeywords).AppendLine();
                            var info = String.Format("{0}\nRemove Shader ShaderVariant: {1}", shader.name, _fullKeywords);
                            Debug.Log(info);
                            return(true);
                        }
                    }
                    return(false);
                }
                    );
                data.Clear();
                backupShaderCompilerData.ForEach(e => data.Add(e));
                backupShaderCompilerData.Clear();
                if (removeCount > 0 && infoSB != null)
                {
                    var info = String.Format(
                        "{0}: {1}-'{2}', {3}, count = {4}\nRemove ShaderVariant Count: {5}\n{6}",
                        shader.name, snippet.shaderType, snippet.passName, snippet.passType, data.Count,
                        removeCount, infoSB.ToString()
                        );
                    Debug.Log(info);
                }
            }
        }