using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Security.Cryptography; using System.Text.Json; using Cosmos.Kernel.System.Graphics; using XFESystem.Browser; using XFESystem.Desktop; using XFESystem.Kernel; using XfePaintBaseline; System.Runtime.Loader.AssemblyLoadContext.Default.Resolving += (_, name) => { string path = Path.Combine(AppContext.BaseDirectory, name.Name + ".dll"); return File.Exists(path) ? System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromAssemblyPath(path) : null; }; Probe.Run(args); static class Probe { [MethodImpl(MethodImplOptions.NoInlining)] public static void Run(string[] args) { string root = Path.GetFullPath(args.Length > 0 ? args[0] : "."); string output = Path.GetFullPath(args.Length > 1 ? args[1] : Path.Combine(root, "build", "PaintPerformance-" + DateTimeOffset.UtcNow.ToUnixTimeMilliseconds())); Directory.CreateDirectory(output); var baseline = Create(typeof(BaselineCanvasAdapter), root); var current = Create(typeof(CanvasAdapter), root); string markdown = File.ReadAllText(Path.Combine(root, "Source/XFESystem.Documents/Assets/Website/Fixtures/WebsiteParity.md")); var results = new List(); foreach (bool dark in new[] { true, false }) { var browser = new BrowserService(); browser.SetCanvas((IDesktopCanvas)current); browser.NavigateDocument(markdown, "WebsiteParity.md", dark); var list = browser.Layout(new(796, 404)); var viewport = new DesktopBounds(0, 0, 796, 404); int[] before = Pixels(baseline), after = Pixels(current); Array.Clear(before); Array.Clear(after); var oldCold = Time(() => BaselinePagePainter.Paint((IDesktopCanvas)baseline, viewport, list)); var newCold = Time(() => BrowserRasterPainter.Paint((IDesktopCanvas)current, viewport, list)); string oldHash = Hash(before), newHash = Hash(after); int differing = before.Zip(after).Count(pair => pair.First != pair.Second); if (differing != 0) throw new InvalidOperationException("Raster pixels changed: " + differing); var snapshot = BrowserPaintSnapshot.Capture(list); Array.Clear(after); snapshot.Paint((IDesktopCanvas)current, viewport); if (Hash(after) != newHash) throw new InvalidOperationException("Frozen DOM-free snapshot changed actual font/CSS raster pixels."); const int iterations = 8; var oldWarm = Time(() => { for (int i = 0; i < iterations; i++) BaselinePagePainter.Paint((IDesktopCanvas)baseline, viewport, list); }); var newWarm = Time(() => { for (int i = 0; i < iterations; i++) BrowserRasterPainter.Paint((IDesktopCanvas)current, viewport, list); }); var native = (CanvasAdapter)current; native.BeginScene(viewport); native.StoreWebFrame(dark ? 1 : 2, 1, 1, viewport, 0, 0); var expected = (int[])after.Clone(); Array.Fill(after, -123); if (!native.TryRestoreWebFrame(dark ? 1 : 2, 1, 1, viewport, 0, 0)) throw new Exception("Native website frame cache missed its full capture."); for (int y = 0; y < 900; y++) for (int x = 0; x < 1600; x++) if (after[y * 1600 + x] != (x < 1592 && y < 808 ? expected[y * 1600 + x] : -123)) throw new Exception("Native website cache pixels changed."); const int restoreIterations = 64; double restoreTime = Time(() => { for (int i = 0; i < restoreIterations; i++) native.TryRestoreWebFrame(dark ? 1 : 2, 1, 1, viewport, 0, 0); }) / restoreIterations; results.Add(new { Dark = dark, Commands = list.Commands.Count, DocumentHeight = list.DocumentHeight, NativeWidth = 1600, NativeHeight = 900, CssViewport = viewport, ScalePercent = 200, BaselineColdMilliseconds = oldCold, CurrentColdMilliseconds = newCold, Iterations = iterations, BaselineWarmMillisecondsPerFrame = oldWarm / iterations, CurrentWarmMillisecondsPerFrame = newWarm / iterations, WarmSpeedup = oldWarm / newWarm, FrameCacheRestoreMillisecondsPerFrame = restoreTime, FrameCacheRestoredPixelsExact = true, BaselinePixelsSha256 = oldHash, CurrentPixelsSha256 = newHash, DifferingPixels = differing, ExactPixels = true, FrozenSnapshotPixelsExact = true, FrozenSnapshotNodeReferences = snapshot.Commands.Count(command => command.Node is not null) }); } var live = (CanvasAdapter)current; var measures = new List(); foreach (int weight in new[] { 400, 700 }) foreach (string family in new[] { "Consolas", "system-ui", "bootstrap-icons" }) foreach (string text in new[] { "WWW iii AV Tof = 42;", "中文文档预览,这是测试。", "using System; var value = 3;", "\uf475\uf30a\uf14d\uf759" }) { var old = (IDesktopWebCanvas)baseline; double expected = old.MeasureWebText(text, 14.25, family, weight); double actual = live.MeasureWebText(text, 14.25, family, weight); if (actual != expected) throw new Exception("Font widths changed."); var a = old.RasterizeWebText(text, 14.25, family, weight); var b = live.RasterizeWebText(text, 14.25, family, weight); if (a.Width != b.Width || a.Height != b.Height || !a.Alpha.AsSpan().SequenceEqual(b.Alpha)) throw new Exception("Font alpha changed."); b.Alpha.AsSpan().Fill(0); var again = live.RasterizeWebText(text, 14.25, family, weight); if (!a.Alpha.AsSpan().SequenceEqual(again.Alpha)) throw new Exception("Writable caller mask corrupted cached data."); measures.Add(new { Family = family, Weight = weight, Text = text, Width = actual, MaskSha256 = Convert.ToHexStringLower(SHA256.HashData(a.Alpha)), Exact = true, WritableCopyIsolated = true }); } var clock = Stopwatch.StartNew(); for (int i = 0; i < 4096; i++) live.MeasureWebText("Unique" + i, 1 + i % 128, "Consolas", 400); for (int i = 0; i < 1100; i++) live.RasterizeWebText("Mask" + i, 14, "Consolas", 400); if (live.CachedWebTextMaskCount > 1024 || live.CachedWebTextMaskBytes > 2097152) throw new Exception("Text mask cache exceeded its bound."); var counts = new Dictionary(); foreach(var pair in new[]{("webGlyphPresence",8192),("webAdvances",8192),("webKernings",4096),("webTextWidths",2048)}) { var dictionary=(System.Collections.IDictionary)typeof(CanvasAdapter).GetField(pair.Item1,BindingFlags.NonPublic|BindingFlags.Instance)!.GetValue(live)!; counts[pair.Item1]=dictionary.Count;if(dictionary.Count>pair.Item2)throw new Exception("Font cache exceeded bound: "+pair.Item1); } var result = new { Schema = "XfeSoftwarePaintPerformance1", Passed = true, HostOnly = true, GuestAcceptance = false, Note = "Executes production software painter, CanvasAdapter and pinned Cosmos TrueTypeFont on a private host framebuffer. Frozen pre-optimization baseline shares the exact same website display list. Timings are host CPU timings, not QEMU FPS or GPU evidence.", Cases = results, FontChecks = measures, CacheBounds = new { live.CachedWebTextMaskCount, live.CachedWebTextMaskBytes, MaximumEntries = 1024, MaximumBytes = 2097152, OtherCacheEntries = counts, live.WebTextMeasureCacheHits, live.WebTextMaskCacheHits, Passed = true }, NativeFrameCache = CheckNativeFrameCache(root), NativeRenderYield = CheckNativeRenderYield(root), NativeAbortAndCoverage = CheckNativeAbortAndCoverage(root), NativeMotionFrameCache = CheckNativeMotionFrameCache(root), NativeEditorPreparationHandoff = CheckEditorPreparationHandoff(root), SourceHashes = new[] { "Source/XFESystem.Browser/BrowserBoxDecoration.cs", "Source/XFESystem.Browser/BrowserCssGradient.cs", "Source/XFESystem.Browser/BrowserClipRasterCanvas.cs", "Source/XFESystem.Kernel/CanvasAdapter.cs", "Source/XFESystem.Desktop/IDesktopWebFrameCache.cs", "Source/XFESystem.Desktop/IDesktopRenderYieldCanvas.cs", "Source/XFESystem.Kernel/NativeKeyboardCapture.cs", "Source/XFESystem.Kernel/NativePointerCapture.cs" } .ToDictionary(path => path, path => Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(Path.Combine(root,path))))) }; string json = JsonSerializer.Serialize(result, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(Path.Combine(output, "Result.json"), json); Console.WriteLine(json); } private static double Time(Action action) { var timer = Stopwatch.StartNew(); action(); return timer.Elapsed.TotalMilliseconds; } private static string Hash(int[] pixels) => Convert.ToHexStringLower(SHA256.HashData(System.Runtime.InteropServices.MemoryMarshal.AsBytes(pixels.AsSpan()))); private static int[] Pixels(object target) => (int[])target.GetType().GetField("pixels", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(target)!; private static object CheckEditorPreparationHandoff(string root) { var prepared = (CanvasAdapter)Create(typeof(CanvasAdapter), root); var main = (CanvasAdapter)Create(typeof(CanvasAdapter), root); var cold = (CanvasAdapter)Create(typeof(CanvasAdapter), root); var mismatch = (CanvasAdapter)Create(typeof(CanvasAdapter), root); void Set(CanvasAdapter target, string name, object value) => typeof(CanvasAdapter).GetField(name, BindingFlags.NonPublic | BindingFlags.Instance)!.SetValue(target, value); int Count(CanvasAdapter target, string name) => ((System.Collections.IDictionary)typeof(CanvasAdapter).GetField(name, BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(target)!).Count; Set(prepared, "preparationOnly", true); Set(mismatch, "scale", 1); Array.Fill(Pixels(prepared), -123); Array.Fill(Pixels(main), -123); string preparedPixels = Hash(Pixels(prepared)), mainPixels = Hash(Pixels(main)); var samples = new[] { (Text: "using System; public class XFE { int value = 42; }", Family: "Consolas", Weight: 400), (Text: "中文 C# Preview", Family: "system-ui", Weight: 700), (Text: "\uf475\uf30a", Family: "bootstrap-icons", Weight: 400) }; foreach (var sample in samples) { prepared.MeasureWebText(sample.Text, 13, sample.Family, sample.Weight); prepared.PrepareEditorText(sample.Text, 13, sample.Family, sample.Weight); prepared.RasterizeWebText(sample.Text, 13, sample.Family, sample.Weight); } prepared.MeasureWebText("中文", 13, "system-ui", 400); main.MeasureWebText("中文", 13, "system-ui", 400); object CjkFont(CanvasAdapter value) { var fonts = (System.Collections.IDictionary)typeof(CanvasAdapter).GetField("webFonts", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(value)!; object face = fonts[10]!; return face.GetType().GetProperty("Font")!.GetValue(face)!; } var preparedFont = (Cosmos.Kernel.System.Graphics.Fonts.TrueTypeFont)CjkFont(prepared); var mainFont = (Cosmos.Kernel.System.Graphics.Fonts.TrueTypeFont)CjkFont(main); if (ReferenceEquals(preparedFont, mainFont) || !ReferenceEquals(preparedFont.Data, mainFont.Data)) throw new Exception("Font resource bytes are duplicated or mutable font caches are shared."); string resourceHash = Convert.ToHexStringLower(SHA256.HashData(preparedFont.Data)); prepared.PrepareEditorText("中文", 13, "system-ui", 400); prepared.RasterizeWebText("中文", 13, "system-ui", 400); main.RasterizeWebText("中文", 13, "system-ui", 400); if (resourceHash != Convert.ToHexStringLower(SHA256.HashData(mainFont.Data))) throw new Exception("The pinned font parser/rasterizer mutated shared font resource bytes."); int warmedGlyphs = Count(prepared, "webGlyphs"); if (warmedGlyphs == 0 || preparedPixels != Hash(Pixels(prepared)) || mainPixels != Hash(Pixels(main))) throw new Exception("Editor preparation did not warm real glyphs without touching pixels."); if (prepared.TryCompleteEditorPreparationHandoff(mismatch) || main.TryCompleteEditorPreparationHandoff(prepared) || prepared.TryCompleteEditorPreparationHandoff(prepared)) throw new Exception("Editor handoff accepted an incompatible canvas."); if (!prepared.TryCompleteEditorPreparationHandoff(main) || !prepared.TryCompleteEditorPreparationHandoff(main) || prepared.TryCompleteEditorPreparationHandoff(cold)) throw new Exception("Editor handoff was not exclusive and idempotent."); if (warmedGlyphs != Count(main, "webGlyphs") || preparedPixels != Hash(Pixels(prepared)) || mainPixels != Hash(Pixels(main))) throw new Exception("Editor handoff lost glyphs or changed pixels."); Array.Clear(Pixels(main)); Array.Clear(Pixels(cold)); main.BeginScene(new(0, 0, 800, 450)); cold.BeginScene(new(0, 0, 800, 450)); long hitsBefore = main.WebTextMeasureCacheHits; int glyphsBefore = Count(main, "webGlyphs"), y = 4; foreach (var sample in samples) { if (main.MeasureWebText(sample.Text, 13, sample.Family, sample.Weight) != cold.MeasureWebText(sample.Text, 13, sample.Family, sample.Weight)) throw new Exception("Prepared metrics differ from the actual main canvas."); main.WebText(sample.Text, 0xAC99EA, 4, y, 13, sample.Family, sample.Weight); cold.WebText(sample.Text, 0xAC99EA, 4, y, 13, sample.Family, sample.Weight); y += 24; } if (Hash(Pixels(main)) != Hash(Pixels(cold)) || glyphsBefore != Count(main, "webGlyphs") || main.WebTextMeasureCacheHits - hitsBefore < samples.Length) throw new Exception("Prepared native glyph handoff changed pixels or missed the scanout glyph cache."); bool rejectedAfterHandoff = false; try { prepared.PrepareEditorText("not owned by worker", 13, "Consolas", 400); } catch (InvalidOperationException) { rejectedAfterHandoff = true; } if (!rejectedAfterHandoff) throw new Exception("Worker preparation remained available after ownership transfer."); var mask = main.RasterizeWebText(samples[0].Text, 13, "Consolas", 400); string maskHash = Convert.ToHexStringLower(SHA256.HashData(mask.Alpha)); mask.Alpha.AsSpan().Clear(); if (maskHash != Convert.ToHexStringLower(SHA256.HashData(main.RasterizeWebText(samples[0].Text, 13, "Consolas", 400).Alpha))) throw new Exception("A writable caller corrupted a transferred mask."); return new { Passed = true, ActualTrueTypeFonts = true, ScalePercent = 200, CssFontSize = 13, NativeGlyphSize = 26, PreparedNativeGlyphs = warmedGlyphs, ExactColdRasterPixels = true, NoGlyphRasterizationAfterHandoff = true, PreparationAndHandoffDoNotWritePixels = true, IncompatibleTargetRejected = true, ExclusiveAndIdempotent = true, WorkerPreparationRejectedAfterHandoff = true, WritableMaskIsolated = true, SharedImmutableCjkBytes = preparedFont.Data.Length, SharedFontResourceHashUnchanged = true, IndependentMutableTrueTypeFontObjects = true }; } private static object CheckNativeFrameCache(string root) { var native = (CanvasAdapter)Create(typeof(CanvasAdapter), root); var pixels = Pixels(native); for (int i = 0; i < pixels.Length; i++) pixels[i] = unchecked(i * 123457); var original = (int[])pixels.Clone(); var bounds = new DesktopBounds(10, 20, 30, 25); native.BeginScene(bounds); native.StoreWebFrame(9, 10, 11, bounds, 0, .25); Array.Fill(pixels, -123); var damage = new DesktopBounds(21, 27, 2, 3); native.BeginScene(damage); if (native.WebPaintClip != damage || !native.TryRestoreWebFrame(9, 10, 11, bounds, 0, .25)) throw new Exception("Native damage restore missed."); for (int y = 0; y < 900; y++) for (int x = 0; x < 1600; x++) if (pixels[y * 1600 + x] != (x >= 42 && x < 46 && y >= 54 && y < 60 ? original[y * 1600 + x] : -123)) throw new Exception("Native damage restore touched incorrect pixels."); Array.Fill(pixels, -123); var moved = bounds with { X = 30, Y = 30 }; native.BeginScene(moved); if (!native.TryRestoreWebFrame(9, 10, 11, moved, 0, .25)) throw new Exception("Window movement invalidated native frame."); for (int y = 0; y < 900; y++) for (int x = 0; x < 1600; x++) if (pixels[y * 1600 + x] != (x >= 60 && x < 120 && y >= 60 && y < 110 ? original[(y - 20) * 1600 + x - 40] : -123)) throw new Exception("Native moved restore pixels changed."); native.BeginScene(damage); native.StoreWebFrame(9, 12, 11, moved, 0, .25); if (native.TryRestoreWebFrame(9, 12, 11, moved, 0, .25) || native.TryRestoreWebFrame(9, 10, 12, moved, 0, .25) || native.TryRestoreWebFrame(9, 10, 11, moved, 0, .5)) throw new Exception("Native stale/partial cache frame was accepted."); if(native.CachedWebFrameCount != 1 || native.CachedWebFrameBytes != 12000 || native.WebFramePixelsRestored != 3024) throw new Exception("Native cache accounting changed."); return new { Passed = true, DamagePixelsExact = true, MovedPixelsExact = true, PartialCaptureRejected = true, RevisionLayoutScrollMissesVerified = true, native.CachedWebFrameCount, native.CachedWebFrameBytes, native.WebFrameRestoreHits, native.WebFramePixelsRestored }; } private static object CheckNativeRenderYield(string root) { NativeKeyboardCapture.Prepare();while(NativeKeyboardCapture.TryRead(out _,out _,out _)){ } NativePointerCapture.Prepare();while(NativePointerCapture.TryRead(out _)){ } NativePointerCapture.Observe(0,0,false,false,false,0,0);while(NativePointerCapture.TryRead(out _)){ } var native=(CanvasAdapter)Create(typeof(CanvasAdapter),root);var pixels=Pixels(native);Array.Fill(pixels,-123); native.BeginScene(new(0,0,800,450));NativeKeyboardCapture.Observe(0x5B,false);bool yielded=false; try{native.BlendCoverage(0xFFFFFF,0,0,1,4,new byte[]{255,255,255,255});}catch(DesktopRenderYieldException){yielded=true;} if(!yielded||pixels.Any(value=>value!=-123)||!NativeKeyboardCapture.HasPendingReports)throw new Exception("Native keyboard render yield consumed input or changed a row."); if(!NativeKeyboardCapture.TryRead(out var scan,out var released,out var lost)||scan!=0x5B||released||lost)throw new Exception("Native keyboard yield altered raw report."); NativePointerCapture.Observe(20,30,false,false,false,0,1); if(native.RenderYieldRequested)throw new Exception("Ordinary pointer motion cancelled native paint."); native.BlendCoverage(0xFFFFFF,0,0,1,1,new byte[]{255}); if(!NativePointerCapture.TryRead(out var motion)||motion.X!=20||motion.Y!=30)throw new Exception("Native paint consumed motion."); NativePointerCapture.Observe(20,30,true,false,false,0,2);yielded=false; try{native.BlendCoverage(0xFFFFFF,0,1,1,1,new byte[]{255});}catch(DesktopRenderYieldException){yielded=true;} if(!yielded||pixels[1600]!=-123||native.FramesPresented!=0||native.CachedWebFrameCount!=0)throw new Exception("Cancelled native row was cached/presented or painted."); if(!NativePointerCapture.TryRead(out var down)||!down.LeftButton)throw new Exception("Native render yield consumed the button make."); NativePointerCapture.Observe(20,30,false,false,false,0,3);while(NativePointerCapture.TryRead(out _)){ } return new{Passed=true,KeyboardAndButtonYield=true,PendingReportsNotConsumed=true,OrdinaryMotionPaints=true, CancelledRowUnmodified=true,CancelledFrameNotCachedOrPresented=true}; } private static object CheckNativeAbortAndCoverage(string root) { var native=(CanvasAdapter)Create(typeof(CanvasAdapter),root); var area=new DesktopBounds(4,3,12,5); native.BeginScene(new(0,0,800,450));native.PreserveTranslatedSceneRegion(area,area.X,area.Y,true);native.EndScenePreservation(); long PendingPixels() { object rectangle=typeof(CanvasAdapter).GetField("unchangedScanout",BindingFlags.NonPublic|BindingFlags.Instance)!.GetValue(native)!; return (long)(int)rectangle.GetType().GetProperty("Width")!.GetValue(rectangle)!*(int)rectangle.GetType().GetProperty("Height")!.GetValue(rectangle)!; } long normalPending=PendingPixels();if(normalPending!=240)throw new Exception("Normal scene end lost the valid scanout optimization."); native.AbortScenePreservation();native.BeginScene(new(0,0,800,450)); if(PendingPixels()!=0)throw new Exception("Cancelled frame retained a stale scanout copy exclusion."); var comparisons=new List();var baseline=(BaselineCanvasAdapter)Create(typeof(BaselineCanvasAdapter),root); int[] old=Pixels(baseline),current=Pixels(native);var random=new Random(74071); for(int test=0;test<6;test++) { for(int i=0;i{for(int i=0;i{for(int i=0;i=12&&x<36&&y>=8&&y<18?red:blue))throw new Exception("Background frame cache overwrote a moved foreground window."); if(native.WebFramePixelsRestored!=4560)throw new Exception("Protected motion pixels were counted as actual restored pixels."); native.Fill(0x0000ff,0,0,40,30);native.StoreWebFrame(2,1,1,viewport,0,0); if(native.WebFrameCaptures!=1)throw new Exception("Protected foreground pixels were captured as a background browser frame."); native.AbortScenePreservation();native.BeginScene(new(0,0,800,450));Array.Fill(pixels,0); if(native.TryRestoreWebFrame(2,1,1,viewport,0,0))throw new Exception("A partly obscured viewport was cached."); if(!native.TryRestoreWebFrame(1,1,1,viewport,0,0))throw new Exception("Rejected motion capture evicted the good frame."); for(int y=0;y<60;y++)for(int x=0;x<80;x++)if(pixels[y*1600+x]!=blue)throw new Exception("Good browser frame contains foreground pixels."); return new{Passed=true,ForegroundPreservedPixels=240,ActualRestoredPixelsBeforeAbort=4560, BackgroundCaptureRejected=true,GoodFramePreserved=true,ActualRestoredPixelsTotal=native.WebFramePixelsRestored, Captures=native.WebFrameCaptures,HostOnly=true,NativeFrameAcceptance=false}; } private static object Create(Type type, string root) { // Avoid firmware/HAL initialization on the host. Only the backing canvas, // font dictionaries and clipping state used by the real painter are set. object target = RuntimeHelpers.GetUninitializedObject(type); const int width = 1600, height = 900; var surface = new Canvas(width, height); int[] pixels = surface.GetBuffer()!; var scene = new int[pixels.Length]; void Set(string name, object value) => type.GetField(name, BindingFlags.NonPublic | BindingFlags.Instance)!.SetValue(target, value); foreach (var field in type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)) { if (field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) field.SetValue(target, Activator.CreateInstance(field.FieldType)); else if(field.FieldType == typeof(DesktopWebFrameCache)) field.SetValue(target,new DesktopWebFrameCache()); } Set("canvas", surface); Set("scale", 2); Set("pixels", pixels); Set("scene", scene); Set("motion", new DesktopSceneMotionBuffer(pixels, scene, width, height)); Set("unicode", File.ReadAllBytes(Path.Combine(root, "assets/branding/console/xfe-unicode.bin"))); var clip = type.GetField("clip", BindingFlags.NonPublic | BindingFlags.Instance)!; clip.SetValue(target, Activator.CreateInstance(clip.FieldType, 0, 0, width, height)); var clips = type.GetField("webClips", BindingFlags.NonPublic | BindingFlags.Instance)!; clips.SetValue(target, Activator.CreateInstance(clips.FieldType)); return target; } }