using System; using System.IO; using System.Linq; using System.Reflection; using HotUpdate.UI; using Newtonsoft.Json; using UnityEditor; using UnityEngine; using UnityEngine.UI; using Object = UnityEngine.Object; public static class VerifyPreviewDeployment { static string Output => Path.GetFullPath(Path.Combine(Application.dataPath, "../../Tools/output/server_release")); const string MaterialPath = "Assets/Bundles/UI/Material/HomeExplorerCutout.mat"; public static string PackedMaterial() { string art = Path.GetFullPath(Path.Combine(Application.dataPath, "../../Tools/cdn_root/wechat/1.0.0/Art")); var shaders = AssetBundle.LoadFromFile(Directory.GetFiles(art, "unityshaders_*.bundle").Single()); var bundle = AssetBundle.LoadFromFile(Directory.GetFiles(art, "share_assets_bundles_ui_material_*.bundle").Single()); Check(bundle != null, "Material bundle unavailable"); try { var material = bundle.LoadAllAssets().Single(m => m.name == "HomeExplorerCutout"); var saved = new SerializedObject(material).FindProperty("m_SavedProperties.m_Colors"); var key = Color.clear; for (int i = 0; i < saved.arraySize; i++) { var pair = saved.GetArrayElementAtIndex(i); if (pair.FindPropertyRelative("first").stringValue == "_Key") key = pair.FindPropertyRelative("second").colorValue; } Directory.CreateDirectory(Output); string json = JsonConvert.SerializeObject(new { shippedKey = new[] { key.r, key.g, key.b }, sourceKey = AssetDatabase.LoadAssetAtPath(MaterialPath).GetColor("_Key").ToString(), hardcodedMagentaMismatch = Vector4.Distance(key, Color.magenta) > .5f }); File.WriteAllText(Path.Combine(Output, "packed_preview.json"), json); return json; } finally { bundle.Unload(true); if (shaders != null) shaders.Unload(true); } } public static string StartLegacyPreview() { Check(EditorApplication.isPlaying, "Play isolated scene first"); var holder = new GameObject("LegacyPreviewHolder"); holder.SetActive(false); var prefab = AssetDatabase.LoadAssetAtPath("Assets/Bundles/UI/Prefab/UI_MainHUD.prefab"); var hud = Object.Instantiate(prefab, holder.transform, false); hud.name = "LegacyPreviewHUD"; var raw = hud.transform.Find("ExplorerStage").GetComponent(); var material = new Material(AssetDatabase.LoadAssetAtPath(MaterialPath)); material.SetColor("_Key", new Color(.07f,.02f,.09f,1)); raw.material = material; holder.SetActive(true); var camera = new GameObject("LegacyPreviewCamera", typeof(Camera)).GetComponent(); camera.clearFlags = CameraClearFlags.SolidColor; camera.backgroundColor = Color.gray; camera.cullingMask = 0; return "Started authored HUD with shipped legacy material key"; } public static string RuntimeStatus() => JsonConvert.SerializeObject(new { roots = Object.FindObjectsOfType(true).Select(p => new { p.name,p.enabled, active=p.gameObject.activeInHierarchy,p.Ready,material=p.GetComponent()?.material?.name, shader=p.GetComponent()?.material?.shader?.name, supported=p.GetComponent()?.material?.shader?.isSupported }).ToArray(), cameras=Object.FindObjectsOfType().Select(c=>c.name).ToArray() }); public static string CheckAndCapture() { var hud = GameObject.Find("LegacyPreviewHUD"); var raw = hud.transform.Find("ExplorerStage").GetComponent(); var presenter = raw.GetComponent(); Check(presenter != null && presenter.Ready, "Preview not ready"); var previewCamera = (Camera)typeof(HomeExplorerPresenter).GetField("_camera", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(presenter); Check(Vector4.Distance(previewCamera.backgroundColor, raw.material.GetColor("_Key")) < .001f, "Camera clear differs from shipped material"); VerifyDiscard(raw.material); var canvas = hud.GetComponent(); var oldMode = canvas.renderMode; var oldCamera = canvas.worldCamera; var camera = GameObject.Find("LegacyPreviewCamera").GetComponent(); var target = new RenderTexture(540, 960, 24); var pixels = new Texture2D(540, 960, TextureFormat.RGB24, false); var previous = RenderTexture.active; try { camera.targetTexture = target; camera.cullingMask = 1 << LayerMask.NameToLayer("UI"); canvas.renderMode = RenderMode.ScreenSpaceCamera; canvas.worldCamera = camera; canvas.planeDistance = 1; Canvas.ForceUpdateCanvases(); camera.Render(); RenderTexture.active = target; pixels.ReadPixels(new Rect(0,0,540,960),0,0); pixels.Apply(); Directory.CreateDirectory(Output); File.WriteAllBytes(Path.Combine(Output,"legacy_preview.png"),pixels.EncodeToPNG()); } finally { RenderTexture.active = previous; camera.targetTexture = null; canvas.renderMode = oldMode; canvas.worldCamera = oldCamera; target.Release(); Object.Destroy(target); Object.Destroy(pixels); } return "PASS: legacy shipped material and camera match; GPU removes the key, foreground retained; screenshot captured"; } static void VerifyDiscard(Material material) { var input = new Texture2D(2,2,TextureFormat.RGBA32,false,true); var output = new RenderTexture(8,8,0,RenderTextureFormat.ARGB32,RenderTextureReadWrite.Linear); var read = new Texture2D(8,8,TextureFormat.RGBA32,false,true); var previous = RenderTexture.active; try { foreach (var key in new[] { new Color(.07f,.02f,.09f,1), Color.magenta }) { var test = new Material(material); test.SetColor("_Key",key); try { input.SetPixels(Enumerable.Repeat(key,4).ToArray()); input.Apply(); output.Create(); RenderTexture.active = output; GL.Clear(true,true,Color.cyan); Graphics.Blit(input,output,test); RenderTexture.active = output; read.ReadPixels(new Rect(0,0,8,8),0,0);read.Apply(); var color = read.GetPixel(4,4); Check(color.r < .05f && color.g > .95f && color.b > .95f,"GPU did not discard background"); Graphics.Blit(Texture2D.whiteTexture,output,test); RenderTexture.active = output; read.ReadPixels(new Rect(0,0,8,8),0,0);read.Apply(); Check(read.GetPixel(4,4).r > .95f,"GPU discarded foreground"); } finally { Object.Destroy(test); } } } finally { RenderTexture.active = previous; output.Release(); Object.Destroy(input); Object.Destroy(output); Object.Destroy(read); } } static void Check(bool pass,string message) { if (!pass) throw new Exception(message); } }