using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using UnityEditor.Experimental.GraphView; using UnityEngine.Analytics; namespace UnityEditor.ShaderGraph { class ShaderGraphAnalytics { const int k_MaxEventsPerHour = 1000; const int k_MaxNumberOfElements = 1000; const string k_VendorKey = "unity.shadergraph"; const string k_EventName = "uShaderGraphUsage"; const string k_TemplateEventName = "uShaderGraphCreateFromTemplate"; [AnalyticInfo(eventName: k_TemplateEventName, vendorKey: k_VendorKey)] internal class ShaderGraphTemplateAnalytic : IAnalytic { [Serializable] struct AnalyticsData : IAnalytic.IData { public string template_name; public string template_path; public string template_category; public string template_guid; public string hdrp_material; public string urp_material; public string builtin_material; public string rt_material; public bool hdrp_vfx; public bool urp_vfx; public bool vfx_legacy; public List additional_terms; } string template_name; string template_path; string template_category; string template_guid; string hdrp_material = string.Empty; string urp_material = string.Empty; string builtin_material = string.Empty; string rt_material = string.Empty; bool hdrp_vfx = false; bool urp_vfx = false; bool vfx_legacy = false; HashSet additional_terms = new(); internal ShaderGraphTemplateAnalytic(GraphViewTemplateDescriptor descriptor) { var path = AssetDatabase.GUIDToAssetPath(descriptor.assetGuid); bool fromUnity = path?.Contains("com.unity") ?? false; template_name = fromUnity ? descriptor.name : "hidden"; template_path = fromUnity ? path : "hidden"; template_category = fromUnity ? descriptor.category : "hidden"; template_guid = descriptor.assetGuid; if (FileUtilities.TryReadGraphDataFromDisk(path, out GraphData graph)) { foreach (var target in graph.activeTargets) { switch (target.GetType().Name) { case "HDTarget": hdrp_material = target.activeSubTarget?.displayName; hdrp_vfx = target.SupportsVFX(); break; case "UniversalTarget": urp_material = target.activeSubTarget?.displayName; urp_vfx = target.SupportsVFX(); break; case "BuiltInTarget": builtin_material = target.activeSubTarget?.displayName; break; case "CustomRenderTextureTarget": rt_material = target.activeSubTarget?.displayName; break; case "VFXTarget": vfx_legacy = target.SupportsVFX(); break; } } foreach (var subdata in graph.SubDatas) { if (!string.IsNullOrEmpty(subdata.displayName)) additional_terms.Add(subdata.displayName); } } } public bool TryGatherData(out IAnalytic.IData data, [NotNullWhen(false)] out Exception error) { data = new AnalyticsData { template_name = template_name, template_path = template_path, template_category = template_category, template_guid = template_guid, hdrp_material = hdrp_material, urp_material = urp_material, builtin_material = builtin_material, rt_material = rt_material, hdrp_vfx = hdrp_vfx, urp_vfx = urp_vfx, vfx_legacy = vfx_legacy, additional_terms = new List(additional_terms) }; error = null; return true; } } public static void SendShaderGraphTemplateEvent(GraphViewTemplateDescriptor descriptor) { if (!EditorAnalytics.enabled) return; var analytic = new ShaderGraphTemplateAnalytic(descriptor); EditorAnalytics.SendAnalytic(analytic); } [AnalyticInfo(eventName: k_EventName, vendorKey: k_VendorKey, maxEventsPerHour:k_MaxEventsPerHour, maxNumberOfElements:k_MaxNumberOfElements)] public class Analytic : IAnalytic { public Analytic(string assetGuid, GraphData graph) { this.assetGuid = assetGuid; this.graph = graph; } [Serializable] struct AnalyticsData : IAnalytic.IData { public string nodes; public int node_count; public string asset_guid; public int subgraph_count; } public bool TryGatherData(out IAnalytic.IData data, out Exception error) { Dictionary nodeTypeAndCount = new Dictionary(); var nodes = graph.GetNodes(); int subgraphCount = 0; foreach (var materialNode in nodes) { string nType = materialNode.GetType().ToString().Split('.').Last(); if (nType == "SubGraphNode") { subgraphCount += 1; } if (!nodeTypeAndCount.ContainsKey(nType)) { nodeTypeAndCount[nType] = 1; } else { nodeTypeAndCount[nType] += 1; } } var jsonRepr = DictionaryToJson(nodeTypeAndCount); data = new AnalyticsData() { nodes = jsonRepr, node_count = nodes.Count(), asset_guid = assetGuid, subgraph_count = subgraphCount }; error = null; return true; } string assetGuid; GraphData graph; }; public static void SendShaderGraphEvent(string assetGuid, GraphData graph) { //The event shouldn't be able to report if this is disabled but if we know we're not going to report //Lets early out and not waste time gathering all the data if (!EditorAnalytics.enabled) return; Analytic analytic = new Analytic(assetGuid, graph); EditorAnalytics.SendAnalytic(analytic); } static string DictionaryToJson(Dictionary dict) { var entries = dict.Select(d => $"\"{d.Key}\":{string.Join(",", d.Value)}"); return "{" + string.Join(",", entries) + "}"; } } }