void UpdateShader(SCShader shader) { if (shader.isReadOnly) { EditorUtility.DisplayDialog("Locked file", "Shader file " + shader.name + " is read-only.", "Ok"); return; } try { // Create backup MakeBackup(shader); if (shader.isShaderGraph) { UpdateShaderGraph(shader); } else { UpdateShaderNonGraph(shader); } // Also update materials CleanMaterials(shader); ScanShader(shader); // Rescan shader // do not include in build (sync with Build View) BuildUpdateShaderKeywordsState(shader); } catch (Exception ex) { Debug.LogError("Unexpected exception caught while updating shader: " + ex.Message); } }
void PruneMaterials(string keywordName) { try { bool requiresSave = false; for (int s = 0; s < shaders.Count; s++) { SCShader shader = shaders[s]; int materialCount = shader.materials.Count; for (int k = 0; k < materialCount; k++) { SCMaterial material = shader.materials[k]; if (material.ContainsKeyword(keywordName)) { Material theMaterial = AssetDatabase.LoadAssetAtPath <Material>(shader.materials[k].path); if (theMaterial == null) { continue; } theMaterial.DisableKeyword(keywordName); EditorUtility.SetDirty(theMaterial); material.RemoveKeyword(keywordName); shader.RemoveKeyword(keywordName); requiresSave = true; } } } if (requiresSave) { AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } } catch (Exception ex) { Debug.Log("Unexpected exception caught while pruning materials: " + ex.Message); } }
void BuildUpdateShaderKeywordsState(SCShader shader) { if (shader == null || shader.keywords == null) { return; } if (shadersBuildInfo == null) { return; } int shadersCount = shadersBuildInfo.shaders.Count; for (int s = 0; s < shadersCount; s++) { ShaderBuildInfo sb = shadersBuildInfo.shaders[s]; if (sb != null && sb.name.Equals(shader.fullName)) { for (int k = 0; k < shader.keywords.Count; k++) { SCKeyword keyword = shader.keywords[k]; sb.ToggleIncludeKeyword(keyword.name, keyword.enabled); } } } shadersBuildInfo.requiresBuild = true; }
void ConvertToLocalNonGraph(SCKeyword keyword, SCShader shader) { string path = shader.path; if (!File.Exists(path)) { return; } string[] lines = File.ReadAllLines(path); bool changed = false; for (int k = 0; k < lines.Length; k++) { // Just convert to local shader_features for now since multi_compile global keywords can be nabled using the Shader global API if (lines[k].IndexOf(SCKeywordLine.PRAGMA_FEATURE_GLOBAL, StringComparison.InvariantCultureIgnoreCase) >= 0 && lines[k].IndexOf(keyword.name, StringComparison.InvariantCultureIgnoreCase) >= 0) { lines[k] = lines[k].Replace(SCKeywordLine.PRAGMA_FEATURE_GLOBAL, SCKeywordLine.PRAGMA_FEATURE_LOCAL); lines[k] = lines[k].Replace(SCKeywordLine.PRAGMA_FEATURE_GLOBAL.ToUpper(), SCKeywordLine.PRAGMA_FEATURE_LOCAL); changed = true; } } if (changed) { MakeBackup(shader); File.WriteAllLines(path, lines, Encoding.UTF8); } }
void ConvertToLocal(SCKeyword keyword, SCShader shader) { // Check total local keyword does not exceed 64 limit int potentialCount = 0; int kwCount = shader.keywords.Count; for (int k = 0; k < kwCount; k++) { SCKeyword kw = shader.keywords[k]; if (!kw.isMultiCompile) { potentialCount++; } } if (potentialCount > 64) { return; } if (shader.isShaderGraph) { ConvertToLocalGraph(keyword, shader); } else { ConvertToLocalNonGraph(keyword, shader); } }
void MakeBackup(SCShader shader) { string backupPath = shader.path + BACKUP_SUFFIX; if (!File.Exists(backupPath)) { AssetDatabase.CopyAsset(shader.path, backupPath); shader.hasBackup = true; } }
void ScanShaderGraph(SCShader shader) { shader.editedByShaderControl = File.Exists(shader.path + BACKUP_SUFFIX); // Reads shader string shaderContents = File.ReadAllText(shader.path, Encoding.UTF8); shaderContents = shaderContents.Replace("UnityEditor.ShaderGraph.ShaderKeyword", "ShaderControl.SCWindow.SerializedKeyword"); ShaderGraphProxy graph = JsonUtility.FromJson <ShaderGraphProxy>(shaderContents); SCShaderPass currentPass = new SCShaderPass(); if (graph.m_SerializedKeywords != null) { for (int k = 0; k < graph.m_SerializedKeywords.Length; k++) { SerializedKeywordData skw = graph.m_SerializedKeywords[k]; if (string.IsNullOrEmpty(skw.JSONnodeData)) { continue; } SerializedKeywordProxy kw = JsonUtility.FromJson <SerializedKeywordProxy>(skw.JSONnodeData); PragmaType pragmaType = PragmaType.Unknown; if (kw.m_KeywordDefinition == SHADER_GRAPH_KEYWORD_DEFINITION_MULTI_COMPILE && kw.m_KeywordScope == SHADER_GRAPH_KEYWORD_SCOPE_GLOBAL) { pragmaType = PragmaType.MultiCompileGlobal; } else if (kw.m_KeywordDefinition == SHADER_GRAPH_KEYWORD_DEFINITION_MULTI_COMPILE && kw.m_KeywordScope == SHADER_GRAPH_KEYWORD_SCOPE_LOCAL) { pragmaType = PragmaType.MultiCompileLocal; } else if (kw.m_KeywordDefinition == SHADER_GRAPH_KEYWORD_DEFINITION_SHADER_FEATURE && kw.m_KeywordScope == SHADER_GRAPH_KEYWORD_SCOPE_GLOBAL) { pragmaType = PragmaType.FeatureGlobal; } else if (kw.m_KeywordDefinition == SHADER_GRAPH_KEYWORD_DEFINITION_SHADER_FEATURE && kw.m_KeywordScope == SHADER_GRAPH_KEYWORD_SCOPE_LOCAL) { pragmaType = PragmaType.FeatureLocal; } SCKeywordLine keywordLine = new SCKeywordLine(); keywordLine.pragmaType = pragmaType; SCKeyword keyword = new SCKeyword(kw.m_DefaultReferenceName, kw.m_Name); keywordLine.Add(keyword); currentPass.Add(keywordLine); } } shader.Add(currentPass); shader.UpdateVariantCount(); }
void ConvertToLocal(SCKeyword keyword, SCShader shader) { // Check total local keyword does not exceed 64 limit int potentialCount = 0; int kwCount = shader.keywords.Count; for (int k = 0; k < kwCount; k++) { SCKeyword kw = shader.keywords[k]; if (!kw.isMultiCompile) { potentialCount++; } } if (potentialCount > 64) { return; } string path = shader.path; if (!File.Exists(path)) { return; } string[] lines = File.ReadAllLines(path); bool changed = false; for (int k = 0; k < lines.Length; k++) { // Just convert to local shader_features for now since multi_compile global keywords can be nabled using the Shader global API if (lines[k].IndexOf(SCKeywordLine.PRAGMA_FEATURE_GLOBAL, StringComparison.InvariantCultureIgnoreCase) >= 0 && lines[k].IndexOf(keyword.name, StringComparison.InvariantCultureIgnoreCase) >= 0) { lines[k] = lines[k].Replace(SCKeywordLine.PRAGMA_FEATURE_GLOBAL, SCKeywordLine.PRAGMA_FEATURE_LOCAL); lines[k] = lines[k].Replace(SCKeywordLine.PRAGMA_FEATURE_GLOBAL.ToUpper(), SCKeywordLine.PRAGMA_FEATURE_LOCAL); changed = true; } } if (changed) { MakeBackup(shader); File.WriteAllLines(path, lines, Encoding.UTF8); } }
void ConvertToLocalGraph(SCKeyword keyword, SCShader shader) { string contents = File.ReadAllText(shader.path, Encoding.UTF8); int i = contents.IndexOf("m_SerializedKeywords"); if (i < 0) { return; } int j = contents.IndexOf("m_SerializedNodes"); if (j < 0) { j = contents.Length - 1; } int pos = contents.IndexOf(keyword.name, i); bool changed = false; if (pos > i && pos < j) { int dataBlockPos = contents.LastIndexOf(JSON_NODE_DATA, pos); if (dataBlockPos > 0) { int scopePos = contents.IndexOf(JSON_KEYWORD_SCOPE, dataBlockPos); if (scopePos > dataBlockPos && scopePos < j) { scopePos += JSON_KEYWORD_SCOPE.Length + 2; int valuePos = contents.IndexOf("1", scopePos); int safetyPos = contents.IndexOf("\"", scopePos); if (valuePos > scopePos && valuePos < safetyPos && safetyPos > valuePos) { contents = contents.Substring(0, valuePos) + "0" + contents.Substring(valuePos + 1); changed = true; } } } } if (changed) { MakeBackup(shader); File.WriteAllText(shader.path, contents, Encoding.UTF8); } }
void ScanShader(SCShader shader) { // Inits shader shader.passes.Clear(); shader.keywords.Clear(); shader.hasBackup = File.Exists(shader.path + BACKUP_SUFFIX); shader.pendingChanges = false; shader.editedByShaderControl = shader.hasBackup; if (shader.path.EndsWith(".shadergraph")) { shader.isShaderGraph = true; ScanShaderGraph(shader); } else { ScanShaderNonGraph(shader); } }
void CleanMaterials(SCShader shader) { // Updates any material using this shader Shader shad = (Shader)AssetDatabase.LoadAssetAtPath <Shader>(shader.path); if (shad != null) { bool requiresSave = false; string[] matGUIDs = AssetDatabase.FindAssets("t:Material"); foreach (string matGUID in matGUIDs) { string matPath = AssetDatabase.GUIDToAssetPath(matGUID); Material mat = (Material)AssetDatabase.LoadAssetAtPath <Material>(matPath); if (mat != null && mat.shader.name.Equals(shad.name)) { foreach (SCKeyword keyword in shader.keywords) { foreach (string matKeyword in mat.shaderKeywords) { if (matKeyword.Equals(keyword.name)) { if (!keyword.enabled && mat.IsKeywordEnabled(keyword.name)) { mat.DisableKeyword(keyword.name); EditorUtility.SetDirty(mat); requiresSave = true; } break; } } } } } if (requiresSave) { AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } } }
void RestoreShader(SCShader shader) { try { string shaderBackupPath = shader.path + BACKUP_SUFFIX; if (!File.Exists(shaderBackupPath)) { EditorUtility.DisplayDialog("Restore shader", "Shader backup is missing!", "OK"); return; } File.Copy(shaderBackupPath, shader.path, true); File.Delete(shaderBackupPath); if (File.Exists(shaderBackupPath + ".meta")) { File.Delete(shaderBackupPath + ".meta"); } AssetDatabase.Refresh(); ScanShader(shader); // Rescan shader UpdateProjectStats(); } catch (Exception ex) { Debug.LogError("Unexpected exception caught while restoring shader: " + ex.Message); } }
void UpdateShaderGraph(SCShader shader) { // Currently shader graph keywords cannot be disabled with Shader Control to avoid graph issues // Just click "Locate" and remove the keyword using the Shader Graph editor }
public void OnProcessShader( Shader shader, ShaderSnippetData snippet, IList <ShaderCompilerData> shaderCompilerData) { bool skipCompilation = false; if (SCWindow.GetEditorPrefBool("QUICK_BUILD", false)) { skipCompilation = true; } if (shadersBuildInfo == null) { string filename = GetStoredDataPath(); shadersBuildInfo = AssetDatabase.LoadAssetAtPath <ShadersBuildInfo>(filename); if (shadersBuildInfo == null) { shadersBuildInfo = ScriptableObject.CreateInstance <ShadersBuildInfo>(); Directory.CreateDirectory(Path.GetDirectoryName(filename)); AssetDatabase.CreateAsset(shadersBuildInfo, filename); EditorUtility.SetDirty(shadersBuildInfo); } } ShaderBuildInfo sb = shadersBuildInfo.GetShader(shader.name); if (sb == null) { sb = new ShaderBuildInfo(); sb.name = shader.name; sb.simpleName = SCShader.GetSimpleName(sb.name); sb.type = snippet.shaderType; string path = AssetDatabase.GetAssetPath(shader); sb.isInternal = string.IsNullOrEmpty(path) || !File.Exists(path); shadersBuildInfo.Add(sb); EditorUtility.SetDirty(shadersBuildInfo); } else if (!sb.includeInBuild) { skipCompilation = true; } int count = shaderCompilerData.Count; for (int i = 0; i < count; ++i) { ShaderKeywordSet ks = shaderCompilerData[i].shaderKeywordSet; foreach (ShaderKeyword kw in ks.GetShaderKeywords()) { #if UNITY_2019_3_OR_NEWER string kname = ShaderKeyword.GetKeywordName(shader, kw); #else string kname = kw.GetKeywordName(); #endif if (string.IsNullOrEmpty(kname)) { continue; } if (!sb.KeywordsIsIncluded(kname)) { shaderCompilerData.RemoveAt(i); count--; i--; break; } else { EditorUtility.SetDirty(shadersBuildInfo); } } } if (skipCompilation) { shaderCompilerData.Clear(); } }
public void OnProcessShader( Shader shader, ShaderSnippetData snippet, IList <ShaderCompilerData> shaderCompilerData) { try { bool skipCompilation = false; if (SCWindow.GetEditorPrefBool("QUICK_BUILD", false)) { skipCompilation = true; } if (shadersBuildInfo == null) { string filename = GetStoredDataPath(); shadersBuildInfo = AssetDatabase.LoadAssetAtPath <ShadersBuildInfo>(filename); if (shadersBuildInfo == null) { return; } } ShaderBuildInfo sb = shadersBuildInfo.GetShader(shader.name); if (sb == null) { sb = new ShaderBuildInfo(); sb.name = shader.name; sb.simpleName = SCShader.GetSimpleName(sb.name); sb.type = snippet.shaderType; string path = AssetDatabase.GetAssetPath(shader); sb.isInternal = string.IsNullOrEmpty(path) || !File.Exists(path); shadersBuildInfo.Add(sb); EditorUtility.SetDirty(shadersBuildInfo); } else if (!sb.includeInBuild) { skipCompilation = true; } int count = shaderCompilerData.Count; for (int i = 0; i < count; ++i) { ShaderKeywordSet ks = shaderCompilerData[i].shaderKeywordSet; foreach (ShaderKeyword kw in ks.GetShaderKeywords()) { #if UNITY_2019_3_OR_NEWER string kname = ShaderKeyword.GetKeywordName(shader, kw); #else string kname = kw.GetName(); #endif if (string.IsNullOrEmpty(kname)) { continue; } if (!sb.KeywordsIsIncluded(kname)) { shaderCompilerData.RemoveAt(i); count--; i--; break; } else { EditorUtility.SetDirty(shadersBuildInfo); } } } if (skipCompilation) { shaderCompilerData.Clear(); return; } } catch (Exception ex) { Debug.LogWarning("Shader Control detected an error during compilation of one shader: " + ex.ToString()); } }
void DrawSVCMgrGUI() { svcSettingsInfo = CheckSVCSettingsStore(svcSettingsInfo); if (null == svcSettingsInfo) { return; } GUILayout.Box(new GUIContent(" Set All Shader Variant Colletion Usage "), titleStyle, GUILayout.ExpandWidth(true)); for (int i = 0; i < svcSettingsInfo.collection.Count; i++) { EditorGUILayout.BeginHorizontal(); { EditorGUILayout.LabelField(string.Format("Set SVC Usage {0}:", (i + 1)), GUILayout.Width(80)); EditorGUI.BeginChangeCheck(); { svcSettingsInfo.collection[i] = (ShaderVariantCollection)EditorGUILayout.ObjectField(svcSettingsInfo.collection[i], typeof(ShaderVariantCollection), false); ShaderVariantUsage usage = (ShaderVariantUsage)EditorGUILayout.EnumPopup("Usage", (ShaderVariantUsage)svcSettingsInfo.usage[i]); svcSettingsInfo.usage[i] = (int)usage; if (GUILayout.Button("¡ª", GUILayout.Width(30))) { //delete in loop svcSettingsInfo.collection.RemoveAt(i); svcSettingsInfo.usage.RemoveAt(i); i--; } } if (EditorGUI.EndChangeCheck()) { EditorUtility.SetDirty(svcSettingsInfo); AssetDatabase.SaveAssets(); } } EditorGUILayout.EndHorizontal(); } EditorGUILayout.BeginHorizontal(); { EditorGUI.BeginChangeCheck(); { if (GUILayout.Button(new GUIContent("Add Shader Variant Collection"))) { svcSettingsInfo.collection.Add(null); svcSettingsInfo.usage.Add(0); } if (EditorGUI.EndChangeCheck()) { EditorUtility.SetDirty(svcSettingsInfo); AssetDatabase.SaveAssets(); } } } EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); EditorGUILayout.Separator(); //if (GUILayout.Button(new GUIContent("Collect SVC in target Scenes", "Quickly scans the project and finds shaders that use keywords."))) //{ // LoadAllScenesCollectMaterial(); // GUIUtility.ExitGUI(); // return; //} if (shaders != null && shaders.Count > 0 && GUILayout.Button(new GUIContent("Clean All Materials", "Removes all disabled keywords in all materials. This option is provided to ensure no existing materials are referencing a disabled shader keyword.\n\nTo disable keywords, first expand any shader from the list and uncheck the unwanted keywords (press 'Save' to modify the shader file and to clean any existing material that uses that specific shader)."), GUILayout.Width(130))) { CleanAllMaterials(); GUIUtility.ExitGUI(); return; } //if (GUILayout.Button("Help", GUILayout.Width(40))) { // ShowHelpWindowProjectView(); //} //EditorGUILayout.EndHorizontal(); //EditorGUILayout.EndVertical(); //EditorGUILayout.Separator(); EditorGUILayout.BeginVertical(blackStyle); EditorGUILayout.BeginHorizontal(); { if (GUILayout.Button(new GUIContent("Save Shader Variant Collection Settings", "save current shader variants"))) { string message = "Save shader variant collection"; string assetPath = EditorUtility.SaveFilePanelInProject("Save Shader Variant Collection", "NewShaderVariants", "shadervariants", message); if (!string.IsNullOrEmpty(assetPath)) { SVCTool.ShaderUtils.SaveCurrentShaderVariantCollection(assetPath); PostProcessSavedSVC(assetPath); } GUIUtility.ExitGUI(); return; } } EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); EditorGUILayout.Separator(); if (shaders != null) { string totalGlobalKeywordsTxt = totalGlobalKeywords != totalKeywords ? " (" + totalGlobalKeywords + " global) " : " "; string statsText; if (totalKeywords == totalUsedKeywords || totalKeywords == 0) { statsText = "Shaders Found: " + totalShaderCount.ToString() + " Shaders Using Keywords: " + shaders.Count.ToString() + "\nKeywords: " + totalKeywords.ToString() + totalGlobalKeywordsTxt + " Variants: " + totalVariants.ToString(); } else { int keywordsPerc = totalUsedKeywords * 100 / totalKeywords; int variantsPerc = totalBuildVariants * 100 / totalVariants; statsText = "Shaders Found: " + totalShaderCount.ToString() + " Shaders Using Keywords: " + shaders.Count.ToString() + "\nUsed Keywords: " + totalUsedKeywords.ToString() + " of " + totalKeywords.ToString() + " (" + keywordsPerc.ToString() + "%) " + totalGlobalKeywordsTxt + " Actual Variants: " + totalBuildVariants.ToString() + " of " + totalVariants.ToString() + " (" + variantsPerc.ToString() + "%)"; } if (totalBuildShaders == 0) { statsText += "\nNote: Total keyword count maybe higher. Use 'Build View' tab to detect additional shaders/keywords used by Unity."; } else if (plusBuildKeywords > 0) { statsText += "\n+ " + plusBuildKeywords + " additional keywords detected in last build."; } EditorGUILayout.HelpBox(statsText, MessageType.Info); EditorGUILayout.Separator(); int shaderCount = shaders.Count; if (shaderCount > 0) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Sort By", GUILayout.Width(100)); SortType prevSortType = sortType; sortType = (SortType)EditorGUILayout.EnumPopup(sortType); if (sortType != prevSortType) { ScanProject(); GUIUtility.ExitGUI(); return; } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Name Filter", GUILayout.Width(100)); projectShaderNameFilter = EditorGUILayout.TextField(projectShaderNameFilter); if (GUILayout.Button(new GUIContent("Clear", "Clear filter."), EditorStyles.miniButton, GUILayout.Width(60))) { projectShaderNameFilter = ""; GUIUtility.keyboardControl = 0; } EditorGUILayout.EndHorizontal(); if (sortType != SortType.Keyword) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Keywords >=", GUILayout.Width(100)); minimumKeywordCount = EditorGUILayout.IntSlider(minimumKeywordCount, 0, maxKeywordsCountFound); EditorGUILayout.EndHorizontal(); } EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Keyword Filter", GUILayout.Width(100)); keywordFilter = EditorGUILayout.TextField(keywordFilter); if (GUILayout.Button(new GUIContent("Clear", "Clear filter."), EditorStyles.miniButton, GUILayout.Width(60))) { keywordFilter = ""; GUIUtility.keyboardControl = 0; } EditorGUILayout.EndHorizontal(); if (sortType != SortType.Material) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Keyword Scope", GUILayout.Width(100)); keywordScopeFilter = (KeywordScopeFilter)EditorGUILayout.EnumPopup(keywordScopeFilter); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Pragma Type", GUILayout.Width(100)); pragmaTypeFilter = (PragmaTypeFilter)EditorGUILayout.EnumPopup(pragmaTypeFilter); EditorGUILayout.EndHorizontal(); if (sortType != SortType.Keyword) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Modified", GUILayout.Width(100)); modifiedStatus = (ModifiedStatus)EditorGUILayout.EnumPopup(modifiedStatus); EditorGUILayout.EndHorizontal(); notIncludedInBuild = EditorGUILayout.ToggleLeft("List Shaders Not Used In Build", notIncludedInBuild); if (notIncludedInBuild) { if (totalBuildShaders == 0) { EditorGUILayout.HelpBox("No build data yet. Perform a build to detect which shaders are being included in your build. Use 'Quick Build' from the Build View tab to make a fast build.", MessageType.Error); } } } if (keywordScopeFilter != KeywordScopeFilter.GlobalKeywords || pragmaTypeFilter != PragmaTypeFilter.ShaderFeature) { if (totalGlobalShaderFeatures > 0) { if (GUILayout.Button(new GUIContent("Click to show " + totalGlobalShaderFeatures + " global keyword(s) found of type 'shader_feature'", "Lists all shaders using global keywords that can be converted to local safely."))) { sortType = SortType.Keyword; keywordScopeFilter = KeywordScopeFilter.GlobalKeywords; pragmaTypeFilter = PragmaTypeFilter.ShaderFeature; keywordFilter = ""; GUIUtility.keyboardControl = 0; minimumKeywordCount = 0; } } } else { if (sortType == SortType.Keyword && totalGlobalShaderFeatures > 1) { if (GUILayout.Button(new GUIContent("Convert all these " + totalGlobalShaderFeatures + " keyword(s) to local", "Converts all global keywords of type Shader_Feature to local keywords, reducing the total keyword usage count.\n A local keyword does not count towards the limit of 256 total keywords in project."))) { ConvertToLocalAll(); EditorUtility.DisplayDialog("Process complete!", "All keywords of type shader_feature have been converted to local.\n\nPlease restart Unity to refresh keyword changes.", "Ok"); ScanProject(); GUIUtility.ExitGUI(); } } } } EditorGUILayout.Separator(); } scrollViewPosProject = EditorGUILayout.BeginScrollView(scrollViewPosProject); if (sortType == SortType.Material) { if (projectMaterials != null) { int matCount = projectMaterials.Count; for (int s = 0; s < matCount; s++) { SCMaterial mat = projectMaterials[s]; int keywordCount = mat.keywords.Count; if (keywordCount < minimumKeywordCount) { continue; } if (!string.IsNullOrEmpty(projectShaderNameFilter) && mat.unityMaterial.name.IndexOf(projectShaderNameFilter, StringComparison.InvariantCultureIgnoreCase) < 0) { continue; } mat.foldout = EditorGUILayout.Foldout(mat.foldout, new GUIContent("Material <b>" + mat.unityMaterial.name + "</b> referencing " + keywordCount + " keywords."), foldoutRTF); if (mat.foldout) { for (int m = 0; m < keywordCount; m++) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("", GUILayout.Width(30)); EditorGUILayout.LabelField(shaderIcon, GUILayout.Width(18)); EditorGUILayout.LabelField(mat.keywords[m].name); if (GUILayout.Button(new GUIContent("Locate", "Locates material in project."), EditorStyles.miniButton, GUILayout.Width(60))) { Selection.activeObject = mat.unityMaterial; EditorGUIUtility.PingObject(mat.unityMaterial); } if (GUILayout.Button(new GUIContent("Prune", "Removes keyword from this material."), EditorStyles.miniButton, GUILayout.Width(60))) { if (EditorUtility.DisplayDialog("Prune Keyword", "Remove this keyword from the material?", "Yes", "No")) { mat.unityMaterial.DisableKeyword(mat.keywords[m].name); EditorUtility.SetDirty(mat.unityMaterial); mat.RemoveKeyword(mat.keywords[m].name); GUIUtility.ExitGUI(); return; } } if (GUILayout.Button(new GUIContent("Prune All", "Removes this keyword from all materials."), EditorStyles.miniButton, GUILayout.Width(80))) { if (EditorUtility.DisplayDialog("Prune All", "Remove this keyword from all materials?", "Yes", "No")) { PruneMaterials(mat.keywords[m].name); UpdateProjectStats(); GUIUtility.ExitGUI(); return; } } EditorGUILayout.EndHorizontal(); } } } } } else if (sortType == SortType.Keyword) { if (keywordView != null) { int kvCount = keywordView.Count; for (int s = 0; s < kvCount; s++) { SCKeyword keyword = keywordView[s].keyword; if (keywordScopeFilter != KeywordScopeFilter.Any && ((keywordScopeFilter == KeywordScopeFilter.GlobalKeywords && !keyword.isGlobal) || (keywordScopeFilter == KeywordScopeFilter.LocalKeywords && keyword.isGlobal))) { continue; } if (pragmaTypeFilter != PragmaTypeFilter.Any && ((pragmaTypeFilter == PragmaTypeFilter.MultiCompile && !keyword.isMultiCompile) || (pragmaTypeFilter == PragmaTypeFilter.ShaderFeature && keyword.isMultiCompile))) { continue; } if (!string.IsNullOrEmpty(keywordFilter) && keyword.name.IndexOf(keywordFilter, StringComparison.InvariantCultureIgnoreCase) < 0) { continue; } EditorGUILayout.BeginHorizontal(); keywordView[s].foldout = EditorGUILayout.Foldout(keywordView[s].foldout, new GUIContent("Keyword <b>" + keywordView[s].keyword + "</b> found in " + keywordView[s].shaders.Count + " shader(s)"), foldoutRTF); GUILayout.FlexibleSpace(); if (!keyword.isMultiCompile && keyword.isGlobal) { if (GUILayout.Button("Convert To Local Keyword", EditorStyles.miniButtonRight, GUILayout.Width(190))) { if (EditorUtility.DisplayDialog("Convert global keyword to local", "Keyword " + keyword.name + " will be converted to local. This means the keyword won't count toward the maximum global keyword limit (256). Continue?", "Ok", "Cancel")) { ConvertToLocal(keyword); ScanProject(); GUIUtility.ExitGUI(); } } } else if (!keyword.isGlobal) { GUILayout.Label("(Local keyword)"); } EditorGUILayout.EndHorizontal(); if (keywordView[s].foldout) { int kvShadersCount = keywordView[s].shaders.Count; for (int m = 0; m < kvShadersCount; m++) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("", GUILayout.Width(30)); EditorGUILayout.LabelField(shaderIcon, GUILayout.Width(18)); EditorGUILayout.LabelField(keywordView[s].shaders[m].name); SCShader shader = keywordView[s].shaders[m]; GUI.enabled = shader.hasSource; if (GUILayout.Button(new GUIContent("Locate", "Locates the shader in the shader view."), EditorStyles.miniButton, GUILayout.Width(60))) { Shader theShader = AssetDatabase.LoadAssetAtPath <Shader>(shader.path); Selection.activeObject = theShader; EditorGUIUtility.PingObject(theShader); } GUI.enabled = true; if (GUILayout.Button(new GUIContent("Filter", "Show shaders using this keyword."), EditorStyles.miniButton, GUILayout.Width(60))) { keywordFilter = keywordView[s].keyword.name; sortType = SortType.VariantsCount; GUIUtility.ExitGUI(); return; } if (!shader.hasBackup) { GUI.enabled = false; } if (GUILayout.Button(new GUIContent("Restore", "Restores shader from the backup copy."), EditorStyles.miniButton)) { RestoreShader(shader); GUIUtility.ExitGUI(); return; } GUI.enabled = true; EditorGUILayout.EndHorizontal(); } } } } } else { int shadersListedCount = 0; for (int s = 0; s < shaderCount; s++) { SCShader shader = shaders[s]; if (shader.keywordEnabledCount < minimumKeywordCount) { continue; } if (modifiedStatus == ModifiedStatus.OnlyModified && !shader.editedByShaderControl) { continue; } if (modifiedStatus == ModifiedStatus.NonModified && shader.editedByShaderControl) { continue; } bool filterByKeywordName = !string.IsNullOrEmpty(keywordFilter); if (filterByKeywordName || keywordScopeFilter != KeywordScopeFilter.Any || pragmaTypeFilter != PragmaTypeFilter.Any) { int kwCount = shader.keywords.Count; bool found = false; for (int w = 0; w < kwCount; w++) { found = true; SCKeyword keyword = shader.keywords[w]; if (filterByKeywordName) { if (keyword.name.IndexOf(keywordFilter, StringComparison.InvariantCultureIgnoreCase) < 0) { found = false; } } if (keywordScopeFilter != KeywordScopeFilter.Any && ((keywordScopeFilter == KeywordScopeFilter.GlobalKeywords && !keyword.isGlobal) || (keywordScopeFilter == KeywordScopeFilter.LocalKeywords && keyword.isGlobal))) { found = false; } if (pragmaTypeFilter != PragmaTypeFilter.Any && ((pragmaTypeFilter == PragmaTypeFilter.MultiCompile && !keyword.isMultiCompile) || (pragmaTypeFilter == PragmaTypeFilter.ShaderFeature && keyword.isMultiCompile))) { found = false; } if (found) { break; } } if (!found) { continue; } } if (!string.IsNullOrEmpty(projectShaderNameFilter) && shader.name.IndexOf(projectShaderNameFilter, StringComparison.InvariantCultureIgnoreCase) < 0) { continue; } if (notIncludedInBuild && totalBuildShaders > 0) { bool included = false; for (int sb = 0; sb < totalBuildShaders; sb++) { if (shadersBuildInfo.shaders[sb].name.Equals(shader.fullName)) { included = true; break; } } if (included) { continue; } } shadersListedCount++; EditorGUILayout.BeginHorizontal(); string shaderName = shader.isReadOnly ? shader.name + " (readonly)" : shader.name; if (shader.hasSource) { shader.foldout = EditorGUILayout.Foldout(shader.foldout, new GUIContent(shaderName + " (" + shader.keywords.Count + " keywords, " + shader.keywordEnabledCount + " used, " + shader.actualBuildVariantCount + " variants)"), shader.editedByShaderControl ? foldoutBold : foldoutNormal); } else { shader.foldout = EditorGUILayout.Foldout(shader.foldout, new GUIContent(shaderName + " (" + shader.keywordEnabledCount + " keywords used by materials)"), foldoutDim); } EditorGUILayout.EndHorizontal(); if (shader.foldout) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("", GUILayout.Width(15)); if (shader.hasSource) { if (GUILayout.Button(new GUIContent("Locate", "Locate the shader in the project panel."), EditorStyles.miniButton)) { Shader theShader = AssetDatabase.LoadAssetAtPath <Shader>(shader.path); Selection.activeObject = theShader; EditorGUIUtility.PingObject(theShader); } if (!shader.isShaderGraph) { if (GUILayout.Button(new GUIContent("Open", "Open the shader with the system default editor."), EditorStyles.miniButton)) { EditorUtility.OpenWithDefaultApp(shader.path); } if (!shader.pendingChanges) { GUI.enabled = false; } if (GUILayout.Button(new GUIContent("Save", "Saves any keyword change to the shader file (disable/enable the keywords just clicking on the toggle next to the keywords shown below). A backup is created in the same folder."), EditorStyles.miniButton)) { UpdateShader(shader); } GUI.enabled = true; } if (!shader.hasBackup) { GUI.enabled = false; } if (GUILayout.Button(new GUIContent("Restore", "Restores shader from the backup copy."), EditorStyles.miniButton)) { RestoreShader(shader); GUIUtility.ExitGUI(); return; } GUI.enabled = true; } else { EditorGUILayout.LabelField("(Shader source not available)"); } if (shader.materials.Count > 0) { if (GUILayout.Button(new GUIContent(shader.showMaterials ? "Hide Materials" : "List Materials", "Show or hide the materials that use these keywords."), EditorStyles.miniButton)) { shader.showMaterials = !shader.showMaterials; GUIUtility.ExitGUI(); return; } if (shader.showMaterials) { EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("", GUILayout.Width(15)); if (GUILayout.Button("Select Materials In Project", EditorStyles.miniButton)) { int matCount = shader.materials.Count; List <Material> allMaterials = new List <Material>(); for (int m = 0; m < matCount; m++) { SCMaterial material = shader.materials[m]; allMaterials.Add(material.unityMaterial); } if (allMaterials.Count > 0) { Selection.objects = allMaterials.ToArray(); EditorGUIUtility.PingObject(allMaterials[0]); } else { EditorUtility.DisplayDialog("Select Materials In Project", "No matching materials found in project.", "Ok"); } } if (GUILayout.Button("Select Materials In Scene", EditorStyles.miniButton)) { int matCount = shader.materials.Count; List <GameObject> gos = new List <GameObject>(); Renderer[] rr = FindObjectsOfType <Renderer>(); foreach (Renderer r in rr) { GameObject go = r.gameObject; if (go == null) { continue; } if ((go.hideFlags & HideFlags.NotEditable) != 0 || (go.hideFlags & HideFlags.HideAndDontSave) != 0) { continue; } Material[] mats = r.sharedMaterials; if (mats == null) { continue; } for (int m = 0; m < matCount; m++) { Material mat = shader.materials[m].unityMaterial; for (int sm = 0; sm < mats.Length; sm++) { Debug.Log(mat.name + " " + mats[sm].name); if (mats[sm] == mat) { gos.Add(go); m = matCount; break; } } } } if (gos.Count > 0) { Selection.objects = gos.ToArray(); } else { EditorUtility.DisplayDialog("Select Materials In Scene", "No matching materials found in objects of this scene.", "Ok"); } } } } EditorGUILayout.EndHorizontal(); for (int k = 0; k < shader.keywords.Count; k++) { SCKeyword keyword = shader.keywords[k]; if (keyword.isUnderscoreKeyword) { continue; } EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("", GUILayout.Width(15)); if (shader.hasSource && !shader.isShaderGraph) { bool prevState = keyword.enabled; keyword.enabled = EditorGUILayout.Toggle(prevState, GUILayout.Width(18)); if (prevState != keyword.enabled) { shader.pendingChanges = true; shader.UpdateVariantCount(); UpdateProjectStats(); GUIUtility.ExitGUI(); return; } } else { EditorGUILayout.Toggle(true, GUILayout.Width(18)); } EditorGUILayout.LabelField(keyword.verboseName); if (keyword.enabled) { if (!shader.hasSource && GUILayout.Button(new GUIContent("Prune Keyword", "Removes the keyword from all materials that reference it."), EditorStyles.miniButton, GUILayout.Width(110))) { if (EditorUtility.DisplayDialog("Prune Keyword", "This option will disable the keyword " + keyword.name + " in all materials that use " + shader.name + " shader.\nDo you want to continue?", "Ok", "Cancel")) { PruneMaterials(keyword.name); UpdateProjectStats(); } } if (!keyword.isMultiCompile && keyword.isGlobal) { if (GUILayout.Button("Convert To Local Keyword", EditorStyles.miniButtonRight, GUILayout.Width(190))) { if (EditorUtility.DisplayDialog("Convert global keyword to local", "Keyword " + keyword.name + " will be converted to local. This means the keyword won't count toward the maximum global keyword limit (256). Continue?", "Ok", "Cancel")) { ConvertToLocal(keyword); ScanProject(); GUIUtility.ExitGUI(); } } } } EditorGUILayout.EndHorizontal(); if (shader.showMaterials) { // show materials using this shader int matCount = shader.materials.Count; for (int m = 0; m < matCount; m++) { SCMaterial material = shader.materials[m]; if (material.ContainsKeyword(keyword.name)) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("", GUILayout.Width(30)); EditorGUILayout.LabelField(matIcon, GUILayout.Width(18)); EditorGUILayout.LabelField(material.unityMaterial.name); if (GUILayout.Button(new GUIContent("Locate", "Locates the material in the project panel."), EditorStyles.miniButton, GUILayout.Width(60))) { Material theMaterial = AssetDatabase.LoadAssetAtPath <Material>(material.path) as Material; Selection.activeObject = theMaterial; EditorGUIUtility.PingObject(theMaterial); } EditorGUILayout.EndHorizontal(); } } } } if (shader.showMaterials) { // show materials using this shader that does not use any keywords bool first = true; int matCount = shader.materials.Count; for (int m = 0; m < matCount; m++) { SCMaterial material = shader.materials[m]; if (material.keywords.Count == 0) { if (first) { first = false; EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("", GUILayout.Width(15)); EditorGUILayout.LabelField("Materials using this shader and no keywords:"); EditorGUILayout.EndHorizontal(); } EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("", GUILayout.Width(15)); EditorGUILayout.LabelField(matIcon, GUILayout.Width(18)); EditorGUILayout.LabelField(material.unityMaterial.name); if (GUILayout.Button(new GUIContent("Locate", "Locates the material in the project panel."), EditorStyles.miniButton, GUILayout.Width(60))) { Material theMaterial = AssetDatabase.LoadAssetAtPath <Material>(material.path) as Material; Selection.activeObject = theMaterial; EditorGUIUtility.PingObject(theMaterial); } EditorGUILayout.EndHorizontal(); } } } } EditorGUILayout.Separator(); } if (shadersListedCount == 0 && !string.IsNullOrEmpty(projectShaderNameFilter)) { EditorGUILayout.HelpBox("No shader matching '" + projectShaderNameFilter + "' name found. Either the shader doesn't exist in the project as source file or the shader does not use explicit keywords.", MessageType.Info); } } EditorGUILayout.EndScrollView(); } }
void UpdateProjectStats() { totalKeywords = 0; totalGlobalKeywords = 0; totalUsedKeywords = 0; totalVariants = 0; totalBuildVariants = 0; totalGlobalShaderFeatures = 0; if (shaders == null) { return; } if (keywordsDict == null) { keywordsDict = new Dictionary <string, SCKeyword>(); } else { keywordsDict.Clear(); } if (uniqueKeywords == null) { uniqueKeywords = new Dictionary <string, List <SCShader> >(); } else { uniqueKeywords.Clear(); } if (uniqueEnabledKeywords == null) { uniqueEnabledKeywords = new Dictionary <string, List <SCShader> >(); } else { uniqueEnabledKeywords.Clear(); } int shadersCount = shaders.Count; for (int k = 0; k < shadersCount; k++) { SCShader shader = shaders[k]; int keywordsCount = shader.keywords.Count; for (int w = 0; w < keywordsCount; w++) { SCKeyword keyword = shader.keywords[w]; List <SCShader> shadersWithThisKeyword; if (!uniqueKeywords.TryGetValue(keyword.name, out shadersWithThisKeyword)) { shadersWithThisKeyword = new List <SCShader>(); uniqueKeywords[keyword.name] = shadersWithThisKeyword; totalKeywords++; if (keyword.isGlobal) { totalGlobalKeywords++; } if (keyword.isGlobal && !keyword.isMultiCompile && keyword.enabled) { totalGlobalShaderFeatures++; } keywordsDict[keyword.name] = keyword; } shadersWithThisKeyword.Add(shader); if (keyword.enabled) { List <SCShader> shadersWithThisKeywordEnabled; if (!uniqueEnabledKeywords.TryGetValue(keyword.name, out shadersWithThisKeywordEnabled)) { shadersWithThisKeywordEnabled = new List <SCShader>(); uniqueEnabledKeywords[keyword.name] = shadersWithThisKeywordEnabled; totalUsedKeywords++; } shadersWithThisKeywordEnabled.Add(shader); } } totalVariants += shader.totalVariantCount; totalBuildVariants += shader.actualBuildVariantCount; } if (keywordView == null) { keywordView = new List <KeywordView>(); } else { keywordView.Clear(); } foreach (KeyValuePair <string, List <SCShader> > kvp in uniqueEnabledKeywords) { SCKeyword kw; if (!keywordsDict.TryGetValue(kvp.Key, out kw)) { continue; } KeywordView kv = new KeywordView { keyword = kw, shaders = kvp.Value }; keywordView.Add(kv); } keywordView.Sort(delegate(KeywordView x, KeywordView y) { return(y.shaders.Count.CompareTo(x.shaders.Count)); }); // Compute which keywords in build are not present in project plusBuildKeywords = 0; if (buildKeywordView != null) { int count = buildKeywordView.Count; for (int k = 0; k < count; k++) { BuildKeywordView bkv = buildKeywordView[k]; if (!uniqueKeywords.ContainsKey(bkv.keyword)) { plusBuildKeywords++; } } } }
void UpdateShaderNonGraph(SCShader shader) { // Reads and updates shader from disk string[] shaderLines = File.ReadAllLines(shader.path); string[] separator = new string[] { " " }; StringBuilder sb = new StringBuilder(); int pragmaControl = 0; shader.editedByShaderControl = false; SCKeywordLine keywordLine = new SCKeywordLine(); bool blockComment = false; for (int k = 0; k < shaderLines.Length; k++) { int lineCommentIndex = shaderLines[k].IndexOf("//"); int blocCommentIndex = shaderLines[k].IndexOf("/*"); int endCommentIndex = shaderLines[k].IndexOf("*/"); if (blocCommentIndex > 0 && (lineCommentIndex > blocCommentIndex || lineCommentIndex < 0)) { blockComment = true; } if (endCommentIndex > blocCommentIndex && (lineCommentIndex > endCommentIndex || lineCommentIndex < 0)) { blockComment = false; } int j = -1; PragmaType pragmaType = PragmaType.Unknown; if (!blockComment) { j = shaderLines[k].IndexOf(PRAGMA_COMMENT_MARK); if (j >= 0) { pragmaControl = 1; } j = shaderLines[k].IndexOf(SCKeywordLine.PRAGMA_MULTICOMPILE_GLOBAL); if (j >= 0) { pragmaType = PragmaType.MultiCompileGlobal; } else { j = shaderLines[k].IndexOf(SCKeywordLine.PRAGMA_FEATURE_GLOBAL); if (j >= 0) { pragmaType = PragmaType.FeatureGlobal; } else { j = shaderLines[k].IndexOf(SCKeywordLine.PRAGMA_MULTICOMPILE_LOCAL); if (j >= 0) { pragmaType = PragmaType.MultiCompileLocal; } else { j = shaderLines[k].IndexOf(SCKeywordLine.PRAGMA_FEATURE_LOCAL); if (j >= 0) { pragmaType = PragmaType.FeatureLocal; } } } } if (pragmaControl != 1 && lineCommentIndex == 0 && shaderLines[k].IndexOf(PRAGMA_DISABLED_MARK) < 0) { // do not process a commented line j = -1; } } if (j >= 0) { if (pragmaControl != 2) { keywordLine.Clear(); } keywordLine.pragmaType = pragmaType; j = shaderLines[k].IndexOf(' ', j + 20) + 1; // first space after pragma declaration if (j >= shaderLines[k].Length) { continue; } // exclude potential comments inside the #pragma line int lastStringPos = shaderLines[k].IndexOf("//", j); if (lastStringPos < 0) { lastStringPos = shaderLines[k].Length; } int length = lastStringPos - j; string[] kk = shaderLines[k].Substring(j, length).Split(separator, StringSplitOptions.RemoveEmptyEntries); // Sanitize keywords for (int i = 0; i < kk.Length; i++) { kk[i] = kk[i].Trim(); } // Act on keywords switch (pragmaControl) { case 1: // Read original keywords for (int s = 0; s < kk.Length; s++) { SCKeyword keyword = shader.GetKeyword(kk[s]); keywordLine.Add(keyword); } pragmaControl = 2; break; case 0: case 2: if (pragmaControl == 0) { for (int s = 0; s < kk.Length; s++) { SCKeyword keyword = shader.GetKeyword(kk[s]); keywordLine.Add(keyword); } } int kCount = keywordLine.keywordCount; int kEnabledCount = keywordLine.keywordsEnabledCount; if (kEnabledCount < kCount) { // write original keywords if (kEnabledCount == 0) { sb.Append(PRAGMA_DISABLED_MARK); } else { sb.Append(PRAGMA_COMMENT_MARK); } shader.editedByShaderControl = true; sb.Append(keywordLine.GetPragma()); if (keywordLine.hasUnderscoreVariant) { sb.Append(PRAGMA_UNDERSCORE); } for (int s = 0; s < kCount; s++) { SCKeyword keyword = keywordLine.keywords[s]; sb.Append(keyword.name); if (s < kCount - 1) { sb.Append(" "); } } sb.AppendLine(); } if (kEnabledCount > 0) { // Write actual keywords sb.Append(keywordLine.GetPragma()); if (keywordLine.hasUnderscoreVariant) { sb.Append(PRAGMA_UNDERSCORE); } for (int s = 0; s < kCount; s++) { SCKeyword keyword = keywordLine.keywords[s]; if (keyword.enabled) { sb.Append(keyword.name); if (s < kCount - 1) { sb.Append(" "); } } } sb.AppendLine(); } pragmaControl = 0; break; } } else { sb.AppendLine(shaderLines[k]); } } // Writes modified shader File.WriteAllText(shader.path, sb.ToString()); AssetDatabase.Refresh(); }
void ScanShaderNonGraph(SCShader shader) { // Reads shader string[] shaderLines = File.ReadAllLines(shader.path); string[] separator = new string[] { " " }; SCShaderPass currentPass = new SCShaderPass(); SCShaderPass basePass = null; int pragmaControl = 0; int pass = -1; bool blockComment = false; SCKeywordLine keywordLine = new SCKeywordLine(); for (int k = 0; k < shaderLines.Length; k++) { string line = shaderLines[k].Trim(); if (line.Length == 0) { continue; } int lineCommentIndex = line.IndexOf("//"); int blocCommentIndex = line.IndexOf("/*"); int endCommentIndex = line.IndexOf("*/"); if (blocCommentIndex > 0 && (lineCommentIndex > blocCommentIndex || lineCommentIndex < 0)) { blockComment = true; } if (endCommentIndex > blocCommentIndex && (lineCommentIndex > endCommentIndex || lineCommentIndex < 0)) { blockComment = false; } if (blockComment) { continue; } string lineUPPER = line.ToUpper(); if (lineUPPER.Equals("PASS") || lineUPPER.StartsWith("PASS ")) { if (pass >= 0) { currentPass.pass = pass; if (basePass != null) { currentPass.Add(basePass.keywordLines); } shader.Add(currentPass); } else if (currentPass.keywordCount > 0) { basePass = currentPass; } currentPass = new SCShaderPass(); pass++; continue; } int j = line.IndexOf(PRAGMA_COMMENT_MARK); if (j >= 0) { pragmaControl = 1; } else { j = line.IndexOf(PRAGMA_DISABLED_MARK); if (j >= 0) { pragmaControl = 3; } } if (lineCommentIndex == 0 && pragmaControl != 1 && pragmaControl != 3) { continue; // do not process lines commented by user } PragmaType pragmaType = PragmaType.Unknown; int offset = 0; j = line.IndexOf(SCKeywordLine.PRAGMA_MULTICOMPILE_GLOBAL); if (j >= 0) { pragmaType = PragmaType.MultiCompileGlobal; offset = SCKeywordLine.PRAGMA_MULTICOMPILE_GLOBAL.Length; } else { j = line.IndexOf(SCKeywordLine.PRAGMA_FEATURE_GLOBAL); if (j >= 0) { pragmaType = PragmaType.FeatureGlobal; offset = SCKeywordLine.PRAGMA_FEATURE_GLOBAL.Length; } else { j = line.IndexOf(SCKeywordLine.PRAGMA_MULTICOMPILE_LOCAL); if (j >= 0) { pragmaType = PragmaType.MultiCompileLocal; offset = SCKeywordLine.PRAGMA_MULTICOMPILE_LOCAL.Length; } else { j = line.IndexOf(SCKeywordLine.PRAGMA_FEATURE_LOCAL); if (j >= 0) { pragmaType = PragmaType.FeatureLocal; offset = SCKeywordLine.PRAGMA_FEATURE_LOCAL.Length; } } } } if (j >= 0) { if (pragmaControl != 2) { keywordLine = new SCKeywordLine(); } keywordLine.pragmaType = pragmaType; // exclude potential comments inside the #pragma line int lastStringPos = line.IndexOf("//", j + offset); if (lastStringPos < 0) { lastStringPos = line.Length; } int length = lastStringPos - j - offset; string[] kk = line.Substring(j + offset, length).Split(separator, StringSplitOptions.RemoveEmptyEntries); // Sanitize keywords for (int i = 0; i < kk.Length; i++) { kk[i] = kk[i].Trim(); } // Act on keywords switch (pragmaControl) { case 1: // Edited by Shader Control line shader.editedByShaderControl = true; // Add original keywords to current line for (int s = 0; s < kk.Length; s++) { keywordLine.Add(shader.GetKeyword(kk[s])); } pragmaControl = 2; break; case 2: // check enabled keywords keywordLine.DisableKeywords(); for (int s = 0; s < kk.Length; s++) { SCKeyword keyword = keywordLine.GetKeyword(kk[s]); if (keyword != null) { keyword.enabled = true; } } currentPass.Add(keywordLine); pragmaControl = 0; break; case 3: // disabled by Shader Control line shader.editedByShaderControl = true; // Add original keywords to current line for (int s = 0; s < kk.Length; s++) { SCKeyword keyword = shader.GetKeyword(kk[s]); keyword.enabled = false; keywordLine.Add(keyword); } currentPass.Add(keywordLine); pragmaControl = 0; break; case 0: // Add keywords to current line for (int s = 0; s < kk.Length; s++) { keywordLine.Add(shader.GetKeyword(kk[s])); } currentPass.Add(keywordLine); break; } } } currentPass.pass = Mathf.Max(pass, 0); if (basePass != null) { currentPass.Add(basePass.keywordLines); } shader.Add(currentPass); shader.UpdateVariantCount(); }
void DrawProjectGUI() { GUILayout.Box(new GUIContent("List of shader files in your project and modify their keywords.\nOnly shaders with source code or referenced by materials are included in this tab.\nUse the <color=orange><b>Build View</b></color> tab for a complete list of shaders, including hidden/internal Unity shaders."), titleStyle); EditorGUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); scanAllShaders = EditorGUILayout.Toggle(new GUIContent("Force scan all shaders", "Also includes shaders that are not located within a Resources folder"), scanAllShaders); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button(new GUIContent("Scan Project Folders", "Quickly scans the project and finds shaders that use keywords."))) { ScanProject(); GUIUtility.ExitGUI(); return; } if (shaders != null && shaders.Count > 0 && GUILayout.Button(new GUIContent("Clean All Materials", "Removes all disabled keywords in all materials. This option is provided to ensure no existing materials are referencing a disabled shader keyword.\n\nTo disable keywords, first expand any shader from the list and uncheck the unwanted keywords (press 'Save' to modify the shader file and to clean any existing material that uses that specific shader)."), GUILayout.Width(120))) { CleanAllMaterials(); GUIUtility.ExitGUI(); return; } if (GUILayout.Button("Help", GUILayout.Width(40))) { ShowHelpWindowProjectView(); } EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); if (shaders != null) { string totalGlobalKeywordsTxt = totalGlobalKeywords != totalKeywords ? " (" + totalGlobalKeywords + " global) " : " "; if (totalKeywords == totalUsedKeywords || totalKeywords == 0) { EditorGUILayout.HelpBox("Shaders Found: " + totalShaderCount.ToString() + " Shaders Using Keywords: " + shaders.Count.ToString() + "\nKeywords: " + totalKeywords.ToString() + totalGlobalKeywordsTxt + " Variants: " + totalVariants.ToString(), MessageType.Info); } else { int keywordsPerc = totalUsedKeywords * 100 / totalKeywords; int variantsPerc = totalBuildVariants * 100 / totalVariants; EditorGUILayout.HelpBox("Shaders Found: " + totalShaderCount.ToString() + " Shaders Using Keywords: " + shaders.Count.ToString() + "\nUsed Keywords: " + totalUsedKeywords.ToString() + " of " + totalKeywords.ToString() + " (" + keywordsPerc.ToString() + "%) " + totalGlobalKeywordsTxt + " Actual Variants: " + totalBuildVariants.ToString() + " of " + totalVariants.ToString() + " (" + variantsPerc.ToString() + "%)", MessageType.Info); } EditorGUILayout.Separator(); int shaderCount = shaders.Count; if (shaderCount > 1) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Sort By", GUILayout.Width(90)); SortType prevSortType = sortType; sortType = (SortType)EditorGUILayout.EnumPopup(sortType); if (sortType != prevSortType) { ScanProject(); EditorGUIUtility.ExitGUI(); return; } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Name Filter", GUILayout.Width(90)); projectShaderNameFilter = EditorGUILayout.TextField(projectShaderNameFilter); if (GUILayout.Button(new GUIContent("Clear", "Clear filter."), EditorStyles.miniButton, GUILayout.Width(60))) { projectShaderNameFilter = ""; GUIUtility.keyboardControl = 0; } EditorGUILayout.EndHorizontal(); if (sortType != SortType.Keyword) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Keywords >=", GUILayout.Width(90)); minimumKeywordCount = EditorGUILayout.IntSlider(minimumKeywordCount, 0, maxKeywordsCountFound); EditorGUILayout.EndHorizontal(); } EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Keyword Filter", GUILayout.Width(90)); keywordFilter = EditorGUILayout.TextField(keywordFilter); if (GUILayout.Button(new GUIContent("Clear", "Clear filter."), EditorStyles.miniButton, GUILayout.Width(60))) { keywordFilter = ""; GUIUtility.keyboardControl = 0; } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Keyword Scope", GUILayout.Width(90)); keywordScopeFilter = (KeywordScopeFilter)EditorGUILayout.EnumPopup(keywordScopeFilter); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Pragma Type", GUILayout.Width(90)); pragmaTypeFilter = (PragmaTypeFilter)EditorGUILayout.EnumPopup(pragmaTypeFilter); EditorGUILayout.EndHorizontal(); if (sortType != SortType.Keyword) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Modified", GUILayout.Width(90)); modifiedStatus = (ModifiedStatus)EditorGUILayout.EnumPopup(modifiedStatus); EditorGUILayout.EndHorizontal(); } if (keywordScopeFilter != KeywordScopeFilter.GlobalKeywords || pragmaTypeFilter != PragmaTypeFilter.ShaderFeature) { if (totalGlobalShaderFeatures > 0) { if (GUILayout.Button(new GUIContent("Click to show " + totalGlobalShaderFeatures + " global keyword(s) found of type 'shader_feature'", "Lists all shaders using global keywords that can be converted to local safely."))) { sortType = SortType.Keyword; keywordScopeFilter = KeywordScopeFilter.GlobalKeywords; pragmaTypeFilter = PragmaTypeFilter.ShaderFeature; keywordFilter = ""; GUIUtility.keyboardControl = 0; minimumKeywordCount = 0; } } } else { if (sortType == SortType.Keyword && totalGlobalShaderFeatures > 1) { if (GUILayout.Button(new GUIContent("Convert all these " + totalGlobalShaderFeatures + " keyword(s) to local", "Converts all global keywords of type Shader_Feature to local keywords, reducing the total keyword usage count.\n A local keyword does not count towards the limit of 256 total keywords in project."))) { ConvertToLocalAll(); EditorUtility.DisplayDialog("Process complete!", "All keywords of type shader_feature have been converted to local.\n\nPlease restart Unity to refresh keyword changes.", "Ok"); ScanProject(); GUIUtility.ExitGUI(); } } } EditorGUILayout.Separator(); } scrollViewPosProject = EditorGUILayout.BeginScrollView(scrollViewPosProject); if (sortType == SortType.Keyword) { if (keywordView != null) { int kvCount = keywordView.Count; for (int s = 0; s < kvCount; s++) { SCKeyword keyword = keywordView[s].keyword; if (keywordScopeFilter != KeywordScopeFilter.Any && ((keywordScopeFilter == KeywordScopeFilter.GlobalKeywords && !keyword.isGlobal) || (keywordScopeFilter == KeywordScopeFilter.LocalKeywords && keyword.isGlobal))) { continue; } if (pragmaTypeFilter != PragmaTypeFilter.Any && ((pragmaTypeFilter == PragmaTypeFilter.MultiCompile && !keyword.isMultiCompile) || (pragmaTypeFilter == PragmaTypeFilter.ShaderFeature && keyword.isMultiCompile))) { continue; } if (!string.IsNullOrEmpty(keywordFilter) && keyword.name.IndexOf(keywordFilter, StringComparison.InvariantCultureIgnoreCase) < 0) { continue; } EditorGUILayout.BeginHorizontal(); keywordView[s].foldout = EditorGUILayout.Foldout(keywordView[s].foldout, new GUIContent("Keyword <b>" + keywordView[s].keyword + "</b> found in " + keywordView[s].shaders.Count + " shader(s)"), foldoutRTF); GUILayout.FlexibleSpace(); if (!keyword.isMultiCompile && keyword.isGlobal) { if (GUILayout.Button("Convert To Local Keyword", EditorStyles.miniButtonRight)) { if (EditorUtility.DisplayDialog("Convert global keyword to local", "Keyword " + keyword.name + " will be converted to local. This means the keyword won't count toward the maximum global keyword limit (256). Continue?", "Ok", "Cancel")) { ConvertToLocal(keyword); ScanProject(); GUIUtility.ExitGUI(); } } } else if (!keyword.isGlobal) { GUILayout.Label("(Local keyword)"); } EditorGUILayout.EndHorizontal(); if (keywordView[s].foldout) { int kvShadersCount = keywordView[s].shaders.Count; for (int m = 0; m < kvShadersCount; m++) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("", GUILayout.Width(30)); EditorGUILayout.LabelField(shaderIcon, GUILayout.Width(18)); EditorGUILayout.LabelField(keywordView[s].shaders[m].name); SCShader shader = keywordView[s].shaders[m]; GUI.enabled = shader.hasSource; if (GUILayout.Button(new GUIContent("Locate", "Locates the shader in the shader view."), EditorStyles.miniButton, GUILayout.Width(60))) { Shader theShader = AssetDatabase.LoadAssetAtPath <Shader>(shader.path); Selection.activeObject = theShader; EditorGUIUtility.PingObject(theShader); } GUI.enabled = true; if (GUILayout.Button(new GUIContent("Filter", "Show shaders using this keyword."), EditorStyles.miniButton, GUILayout.Width(60))) { keywordFilter = keywordView[s].keyword.name; sortType = SortType.VariantsCount; GUIUtility.ExitGUI(); return; } if (!shader.hasBackup) { GUI.enabled = false; } if (GUILayout.Button(new GUIContent("Restore", "Restores shader from the backup copy."), EditorStyles.miniButton)) { RestoreShader(shader); GUIUtility.ExitGUI(); return; } GUI.enabled = true; EditorGUILayout.EndHorizontal(); } } } } } else { int shadersListedCount = 0; for (int s = 0; s < shaderCount; s++) { SCShader shader = shaders[s]; if (shader.keywordEnabledCount < minimumKeywordCount) { continue; } if (modifiedStatus == ModifiedStatus.OnlyModified && !shader.editedByShaderControl) { continue; } if (modifiedStatus == ModifiedStatus.NonModified && shader.editedByShaderControl) { continue; } bool filterByKeywordName = !string.IsNullOrEmpty(keywordFilter); if (filterByKeywordName || keywordScopeFilter != KeywordScopeFilter.Any || pragmaTypeFilter != PragmaTypeFilter.Any) { int kwCount = shader.keywords.Count; bool found = false; for (int w = 0; w < kwCount; w++) { found = true; SCKeyword keyword = shader.keywords[w]; if (filterByKeywordName) { if (keyword.name.IndexOf(keywordFilter, StringComparison.InvariantCultureIgnoreCase) < 0) { found = false; } } if (keywordScopeFilter != KeywordScopeFilter.Any && ((keywordScopeFilter == KeywordScopeFilter.GlobalKeywords && !keyword.isGlobal) || (keywordScopeFilter == KeywordScopeFilter.LocalKeywords && keyword.isGlobal))) { found = false; } if (pragmaTypeFilter != PragmaTypeFilter.Any && ((pragmaTypeFilter == PragmaTypeFilter.MultiCompile && !keyword.isMultiCompile) || (pragmaTypeFilter == PragmaTypeFilter.ShaderFeature && keyword.isMultiCompile))) { found = false; } if (found) { break; } } if (!found) { continue; } } if (!string.IsNullOrEmpty(projectShaderNameFilter) && shader.name.IndexOf(projectShaderNameFilter, StringComparison.InvariantCultureIgnoreCase) < 0) { continue; } shadersListedCount++; EditorGUILayout.BeginHorizontal(); string shaderName = shader.isReadOnly ? shader.name + " (readonly)" : shader.name; if (shader.hasSource) { shader.foldout = EditorGUILayout.Foldout(shader.foldout, new GUIContent(shaderName + " (" + shader.keywords.Count + " keywords, " + shader.keywordEnabledCount + " used, " + shader.actualBuildVariantCount + " variants)"), shader.editedByShaderControl ? foldoutBold : foldoutNormal); } else { shader.foldout = EditorGUILayout.Foldout(shader.foldout, new GUIContent(shaderName + " (" + shader.keywordEnabledCount + " keywords used by materials)"), foldoutDim); } EditorGUILayout.EndHorizontal(); if (shader.foldout) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("", GUILayout.Width(15)); if (shader.hasSource) { if (GUILayout.Button(new GUIContent("Locate", "Locate the shader in the project panel."), EditorStyles.miniButton)) { Shader theShader = AssetDatabase.LoadAssetAtPath <Shader>(shader.path); Selection.activeObject = theShader; EditorGUIUtility.PingObject(theShader); } if (GUILayout.Button(new GUIContent("Open", "Open the shader with the system default editor."), EditorStyles.miniButton)) { EditorUtility.OpenWithDefaultApp(shader.path); } if (!shader.pendingChanges) { GUI.enabled = false; } if (GUILayout.Button(new GUIContent("Save", "Saves any keyword change to the shader file (disable/enable the keywords just clicking on the toggle next to the keywords shown below). A backup is created in the same folder."), EditorStyles.miniButton)) { UpdateShader(shader); } GUI.enabled = true; if (!shader.hasBackup) { GUI.enabled = false; } if (GUILayout.Button(new GUIContent("Restore", "Restores shader from the backup copy."), EditorStyles.miniButton)) { RestoreShader(shader); GUIUtility.ExitGUI(); return; } GUI.enabled = true; } else { EditorGUILayout.LabelField("(Shader source not available)"); } if (shader.materials.Count > 0) { if (GUILayout.Button(new GUIContent(shader.showMaterials ? "Hide Materials" : "List Materials", "Show or hide the materials that use these keywords."), EditorStyles.miniButton)) { shader.showMaterials = !shader.showMaterials; GUIUtility.ExitGUI(); return; } if (shader.showMaterials) { EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("", GUILayout.Width(15)); if (GUILayout.Button("Select Materials In Project", EditorStyles.miniButton)) { int matCount = shader.materials.Count; List <Material> allMaterials = new List <Material>(); for (int m = 0; m < matCount; m++) { SCMaterial material = shader.materials[m]; allMaterials.Add(material.unityMaterial); } if (allMaterials.Count > 0) { Selection.objects = allMaterials.ToArray(); EditorGUIUtility.PingObject(allMaterials[0]); } else { EditorUtility.DisplayDialog("Select Materials In Project", "No matching materials found in project.", "Ok"); } } if (GUILayout.Button("Select Materials In Scene", EditorStyles.miniButton)) { int matCount = shader.materials.Count; List <GameObject> gos = new List <GameObject>(); Renderer[] rr = FindObjectsOfType <Renderer>(); foreach (Renderer r in rr) { GameObject go = r.gameObject; if (go == null) { continue; } if ((go.hideFlags & HideFlags.NotEditable) != 0 || (go.hideFlags & HideFlags.HideAndDontSave) != 0) { continue; } Material[] mats = r.sharedMaterials; if (mats == null) { continue; } for (int m = 0; m < matCount; m++) { Material mat = shader.materials[m].unityMaterial; for (int sm = 0; sm < mats.Length; sm++) { Debug.Log(mat.name + " " + mats[sm].name); if (mats[sm] == mat) { gos.Add(go); m = matCount; break; } } } } if (gos.Count > 0) { Selection.objects = gos.ToArray(); } else { EditorUtility.DisplayDialog("Select Materials In Scene", "No matching materials found in objects of this scene.", "Ok"); } } } } EditorGUILayout.EndHorizontal(); for (int k = 0; k < shader.keywords.Count; k++) { SCKeyword keyword = shader.keywords[k]; if (keyword.isUnderscoreKeyword) { continue; } EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("", GUILayout.Width(15)); if (shader.hasSource) { bool prevState = keyword.enabled; keyword.enabled = EditorGUILayout.Toggle(prevState, GUILayout.Width(18)); if (prevState != keyword.enabled) { shader.pendingChanges = true; shader.UpdateVariantCount(); UpdateProjectStats(); GUIUtility.ExitGUI(); return; } } else { EditorGUILayout.Toggle(true, GUILayout.Width(18)); } if (!keyword.enabled) { EditorGUILayout.LabelField(keyword.verboseName); } else { EditorGUILayout.LabelField(keyword.verboseName); if (!shader.hasSource && GUILayout.Button(new GUIContent("Prune Keyword", "Removes the keyword from all materials that reference it."), EditorStyles.miniButton, GUILayout.Width(110))) { if (EditorUtility.DisplayDialog("Prune Keyword", "This option will disable the keyword " + keyword.name + " in all materials that use " + shader.name + " shader.\nDo you want to continue?", "Ok", "Cancel")) { PruneMaterials(shader, keyword.name); UpdateProjectStats(); } } } EditorGUILayout.EndHorizontal(); if (shader.showMaterials) { // show materials using this shader int matCount = shader.materials.Count; for (int m = 0; m < matCount; m++) { SCMaterial material = shader.materials[m]; if (material.ContainsKeyword(keyword.name)) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("", GUILayout.Width(30)); EditorGUILayout.LabelField(matIcon, GUILayout.Width(18)); EditorGUILayout.LabelField(material.unityMaterial.name); if (GUILayout.Button(new GUIContent("Locate", "Locates the material in the project panel."), EditorStyles.miniButton, GUILayout.Width(60))) { Material theMaterial = AssetDatabase.LoadAssetAtPath <Material>(material.path) as Material; Selection.activeObject = theMaterial; EditorGUIUtility.PingObject(theMaterial); } EditorGUILayout.EndHorizontal(); } } } } if (shader.showMaterials) { // show materials using this shader that does not use any keywords bool first = true; int matCount = shader.materials.Count; for (int m = 0; m < matCount; m++) { SCMaterial material = shader.materials[m]; if (material.keywords.Count == 0) { if (first) { first = false; EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("", GUILayout.Width(15)); EditorGUILayout.LabelField("Materials using this shader and no keywords:"); EditorGUILayout.EndHorizontal(); } EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("", GUILayout.Width(15)); EditorGUILayout.LabelField(matIcon, GUILayout.Width(18)); EditorGUILayout.LabelField(material.unityMaterial.name); if (GUILayout.Button(new GUIContent("Locate", "Locates the material in the project panel."), EditorStyles.miniButton, GUILayout.Width(60))) { Material theMaterial = AssetDatabase.LoadAssetAtPath <Material>(material.path) as Material; Selection.activeObject = theMaterial; EditorGUIUtility.PingObject(theMaterial); } EditorGUILayout.EndHorizontal(); } } } } EditorGUILayout.Separator(); } if (shadersListedCount == 0 && !string.IsNullOrEmpty(projectShaderNameFilter)) { EditorGUILayout.HelpBox("No shader matching '" + projectShaderNameFilter + "' name found. Either the shader doesn't exist in the project as source file or the shader does not use explicit keywords.", MessageType.Info); } } EditorGUILayout.EndScrollView(); } }
void ScanProject() { try { if (shaders == null) { shaders = new List <SCShader>(); } else { shaders.Clear(); } // Add shaders from Resources folder string[] guids = AssetDatabase.FindAssets("t:Shader"); totalShaderCount = guids.Length; for (int k = 0; k < totalShaderCount; k++) { string guid = guids[k]; string path = AssetDatabase.GUIDToAssetPath(guid); if (path != null) { string pathUpper = path.ToUpper(); if (scanAllShaders || pathUpper.Contains("\\RESOURCES\\") || pathUpper.Contains("/RESOURCES/")) // this shader will be included in build { Shader unityShader = AssetDatabase.LoadAssetAtPath <Shader>(path); if (unityShader != null) { SCShader shader = new SCShader(); shader.fullName = unityShader.name; shader.name = SCShader.GetSimpleName(shader.fullName); // Path.GetFileNameWithoutExtension(path); shader.path = path; string shaderGUID = path + "/" + unityShader.name; shader.GUID = shaderGUID; ScanShader(shader); if (shader.keywords.Count > 0) { shaders.Add(shader); } } } } } // Load and reference materials Dictionary <string, SCShader> shaderCache = new Dictionary <string, SCShader>(shaders.Count); shaders.ForEach(shader => { shaderCache.Add(shader.GUID, shader); }); string[] matGuids = AssetDatabase.FindAssets("t:Material"); if (projectMaterials == null) { projectMaterials = new List <SCMaterial>(); } else { projectMaterials.Clear(); } for (int k = 0; k < matGuids.Length; k++) { string matGUID = matGuids[k]; string matPath = AssetDatabase.GUIDToAssetPath(matGUID); Material mat = AssetDatabase.LoadAssetAtPath <Material>(matPath); if (mat.shader == null) { continue; } SCMaterial scMat = new SCMaterial(mat, matPath, matGUID); scMat.SetKeywords(mat.shaderKeywords); if (mat.shaderKeywords != null && mat.shaderKeywords.Length > 0) { projectMaterials.Add(scMat); } string path = AssetDatabase.GetAssetPath(mat.shader); string shaderGUID = path + "/" + mat.shader.name; SCShader shader; if (!shaderCache.TryGetValue(shaderGUID, out shader)) { if (mat.shaderKeywords == null || mat.shaderKeywords.Length == 0) { continue; } Shader shad = AssetDatabase.LoadAssetAtPath <Shader>(path); // add non-sourced shader shader = new SCShader(); shader.isReadOnly = IsFileWritable(path); shader.GUID = shaderGUID; if (shad != null) { shader.fullName = shad.name; shader.name = SCShader.GetSimpleName(shader.fullName); shader.path = path; ScanShader(shader); } else { shader.fullName = mat.shader.name; shader.name = SCShader.GetSimpleName(shader.fullName); } shaders.Add(shader); shaderCache.Add(shaderGUID, shader); totalShaderCount++; } shader.materials.Add(scMat); shader.AddKeywordsByName(mat.shaderKeywords); } // sort materials by name projectMaterials.Sort(CompareMaterialsName); // refresh variant and keywords count due to potential additional added keywords from materials (rogue keywords) and shader features count maxKeywordsCountFound = 0; shaders.ForEach((SCShader shader) => { if (shader.keywordEnabledCount > maxKeywordsCountFound) { maxKeywordsCountFound = shader.keywordEnabledCount; } shader.UpdateVariantCount(); }); switch (sortType) { case SortType.VariantsCount: shaders.Sort((SCShader x, SCShader y) => { return(y.actualBuildVariantCount.CompareTo(x.actualBuildVariantCount)); }); break; case SortType.EnabledKeywordsCount: shaders.Sort((SCShader x, SCShader y) => { return(y.keywordEnabledCount.CompareTo(x.keywordEnabledCount)); }); break; case SortType.ShaderFileName: shaders.Sort((SCShader x, SCShader y) => { return(x.name.CompareTo(y.name)); }); break; } UpdateProjectStats(); } catch (Exception ex) { Debug.LogError("Unexpected exception caught while scanning project: " + ex.Message); } }