XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Diagnostics;
using System.Reflection;
using System.Security.Cryptography;
using System.Text.Json;
using XFESystem.Browser;
using XFESystem.Desktop;
using XFESystem.Documents;

// Host stage timing only. This does not measure the native framebuffer/GPU and
// never opens a host browser, changes website resources or disables JavaScript.
var root = Path.GetFullPath(args.Length > 0 ? args[0] : ".");
var label = args.Length > 1 ? args[1] : "Current";
if (args.Contains("--loading", StringComparer.Ordinal)) { LoadingProfile.Run(root, label); return; }
if (args.Contains("--monaco", StringComparer.Ordinal)) { MonacoProfile.Run(root, label, args.Contains("--regex", StringComparer.Ordinal)); return; }
var output = Path.Combine(root, "build/PreviewPerformance", label + "-" + DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
Directory.CreateDirectory(output);
var inputs = new[] { "Source/XFESystem.Browser", "Source/XFESystem.Documents" }
    .SelectMany(directory => Directory.EnumerateFiles(Path.Combine(root, directory), "*.cs", SearchOption.AllDirectories))
    .Where(path => !path.Contains(Path.DirectorySeparatorChar + "obj" + Path.DirectorySeparatorChar) && !path.Contains(Path.DirectorySeparatorChar + "bin" + Path.DirectorySeparatorChar))
    .Order(StringComparer.Ordinal).ToDictionary(path => path, Hash);
var fixtures = new[]
{
    ("PlainMarkdown.md", "# Preview\n\nHello **XFEstudio**.\n\nSimple document without code or math."),
    ("CSharp.cs", "using System;\nnamespace XFEstudio;\npublic sealed class Preview { public static void Main() => Console.WriteLine(\"XFE\"); }"),
    ("WebsiteParity.md", WebsiteAssets.ReadText("Fixtures/WebsiteParity.md")),
    ("Math.md", "# Math\n\nInline $a+b=c$.\n\n$$\\frac{a}{b}+\\sqrt{x}$$"),
    ("Diagram.md", "# Diagram\n\n```mermaid\nflowchart LR\nA[Markdown] --> B[Renderer]\n```")
};
var rows = new List<object>();
foreach (var (name, source) in fixtures)
{
    var builder = new DocumentPreviewBuilder(new());
    for (int attempt = 1; attempt <= 3; attempt++)
    {
        var clock = Stopwatch.StartNew();
        var html = name.EndsWith(".md", StringComparison.OrdinalIgnoreCase) ? builder.MarkdownWeb(source, name) : builder.CodeWeb(source, name);
        double htmlMs = clock.Elapsed.TotalMilliseconds;
        var trace = new List<object>(); var scriptStarts = new Dictionary<string, double>(); var scriptDurations = new List<double>();
        var nav = Stopwatch.StartNew();
        var browser = new BrowserService { Trace = line =>
        {
            double at = nav.Elapsed.TotalMilliseconds; var split = line.Trim().Split('|');
            if (split[0] == "XFE_BROWSER_SCRIPT_BEGIN") scriptStarts[split[1]] = at;
            double? duration = split[0] == "XFE_BROWSER_SCRIPT_END" && scriptStarts.TryGetValue(split[1], out var start) ? at - start : null;
            if (duration.HasValue) scriptDurations.Add(duration.Value);
            trace.Add(new { AtMilliseconds = at, DurationMilliseconds = duration, Line = line.Trim() });
        } };
        using var canvas = new WindowsTextCanvas(Path.Combine(root, "assets/fonts/cascadia-mono-2407.24/CascadiaMono-Regular.ttf"), 796, 404);
        browser.SetCanvas(canvas); browser.ResizeViewport(796, 404);
        clock.Restart(); var page = browser.NavigateDocument(source, name); double navigateMs = clock.Elapsed.TotalMilliseconds;
        var frames = new List<object>();
        for (int frame = 1; frame <= 12; frame++)
        {
            clock.Restart(); bool changed = browser.AdvanceTime(frame * 20); frames.Add(new { Frame = frame, Changed = changed, Milliseconds = clock.Elapsed.TotalMilliseconds });
        }
        clock.Restart(); var layout = browser.Layout(new(796, 404)); double layoutMs = clock.Elapsed.TotalMilliseconds;
        clock.Restart(); browser.Paint(canvas, new(0, 0, 796, 404), 0); double paintMs = clock.Elapsed.TotalMilliseconds;
        var scroll = new List<object>();
        for (int index = 1; index <= 8; index++)
        {
            clock.Restart(); browser.Paint(canvas, new(0, 0, 796, 404), index * 50);
            scroll.Add(new { ScrollY = index * 50, Milliseconds = clock.Elapsed.TotalMilliseconds });
        }
        var engine = (BrowserLayoutEngine)typeof(BrowserService).GetField("layoutEngine", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(browser)!;
        var runtime = typeof(BrowserService).GetField("runtime", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(browser);
        long RuntimeCount(string property) => runtime is null ? 0 : (long)typeof(JavaScriptRuntime).GetProperty(property, BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(runtime)!;
        bool successful = !browser.CurrentPage.Text.Contains("JavaScript error:", StringComparison.Ordinal) &&
            !browser.CurrentPage.Text.Contains("Event loop failed:", StringComparison.Ordinal) &&
            browser.LoadedResources.Count(resource => resource.Kind == "script") >= 13;
        rows.Add(new
        {
            Name = name, Attempt = attempt, Successful = successful, HtmlGenerationMilliseconds = htmlMs, HtmlCharacters = html.Html.Length,
            NavigationMilliseconds = navigateMs, ScriptEvaluationMilliseconds = scriptDurations.Sum(), Frames = frames,
            LayoutMilliseconds = layoutMs, PaintMilliseconds = paintMs, Scroll = scroll,
            engine.CssParseGeneration, engine.CssReuseCount, LayoutBoxes = layout.Boxes.Count, DisplayCommands = layout.Commands.Count,
            ViewportPositionedBoxes = layout.Boxes.Where(box => box.Style.Position is "fixed" or "sticky")
                .Select(box => new { Position = box.Style.Position, box.Visible, Tag = box.Element?.LocalName, Id = box.Element?.Id, Classes = box.Element?.ClassName, box.BorderBox }).ToArray(),
            PreparedCacheHits = RuntimeCount("PackagedScriptCacheHits"), PreparedScripts = RuntimeCount("PackagedScriptPreparations"),
            ResourceCharacters = browser.LoadedResources.Sum(resource => resource.Characters), Resources = browser.LoadedResources, Trace = trace,
            PageText = browser.CurrentPage.Text, browser.Revision, canvas.MeasureCalls, canvas.RasterCalls
        });
        Console.WriteLine(JsonSerializer.Serialize(new { Name = name, Attempt = attempt, Successful = successful, NavigationMilliseconds = navigateMs, ScriptMilliseconds = scriptDurations.Sum(), LayoutMilliseconds = layoutMs, PaintMilliseconds = paintMs }));
    }
}
var sourceUnchanged = inputs.All(pair => pair.Value == Hash(pair.Key));
File.WriteAllText(Path.Combine(output, "Result.json"), JsonSerializer.Serialize(new
{
    Kind = "ProductionPreviewPipelineHostProfile", ProductionBrowserServiceUsed = true, HostOnly = true, NativeAcceptance = false,
    HostRaster = "Offscreen Windows GDI+; not the native rasterizer and not GPU acceleration", SourceUnchangedDuringRun = sourceUnchanged,
    BrowserAssemblySha256 = Hash(typeof(BrowserService).Assembly.Location),
    Sources = inputs.Select(pair => new { Path = Path.GetRelativePath(root, pair.Key).Replace('\\', '/'), Sha256 = pair.Value }), Rows = rows
}, new JsonSerializerOptions { WriteIndented = true }));
Console.WriteLine(Path.Combine(output, "Result.json"));
if (!sourceUnchanged) Environment.ExitCode = 1;
static string Hash(string path) => Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(path)));