summaryrefslogtreecommitdiff
path: root/test/pdf_bench.zig
diff options
context:
space:
mode:
Diffstat (limited to 'test/pdf_bench.zig')
-rw-r--r--test/pdf_bench.zig97
1 files changed, 85 insertions, 12 deletions
diff --git a/test/pdf_bench.zig b/test/pdf_bench.zig
index 1325def5..fb911bee 100644
--- a/test/pdf_bench.zig
+++ b/test/pdf_bench.zig
@@ -7,9 +7,11 @@
//!
//! The benchmark links the real src/pdf.zig wrapper and C exception bridge in
//! ReleaseFast. Timed regions include MuPDF rasterization directly into the
-//! allocator-owned RGBA output; checksum/free/reporting stay outside. "cold"
-//! means a fresh Document and MuPDF page cache (the OS filesystem cache is
-//! deliberately uncontrolled), while warm scenarios retain one Document.
+//! allocator-owned RGBA output; tint scenarios additionally include the exact
+//! in-place PDF transform used by the core. Checksum/free/reporting stay
+//! outside. "cold" means a fresh Document and MuPDF page cache (the OS
+//! filesystem cache is deliberately uncontrolled), while warm scenarios
+//! retain one Document.
const std = @import("std");
const pdf = @import("mupdf");
@@ -69,13 +71,29 @@ const Scenario = struct {
id: []const u8,
lifecycle: Lifecycle,
decoration: Decoration,
+ tint: pdf.TintMode = .disabled,
};
+// Fixed benchmark colors, independent of user config and host defaults. They
+// are Pardes's helix page background/foreground and make run-to-run tint
+// checksums a stable optimization target.
+const tint_colors: pdf.TintColors = .{
+ .background = .{ 0x08, 0x08, 0x08 },
+ .foreground = .{ 0xbd, 0xbd, 0xbd },
+};
+
+// Append only: baseline readers and the cross-scenario checks below rely on
+// the original four scenarios retaining their indices and meaning.
const scenarios = [_]Scenario{
.{ .id = "cold-document-plain", .lifecycle = .cold_document_page, .decoration = .plain },
.{ .id = "warm-page-plain", .lifecycle = .warm_page, .decoration = .plain },
.{ .id = "warm-page-highlighted", .lifecycle = .warm_page, .decoration = .highlighted },
.{ .id = "warm-document-pass", .lifecycle = .warm_document_pass, .decoration = .plain },
+ // The checked-in design page is grayscale. Reuse the original deterministic
+ // highlight decoration here so filtered/full exercise source chroma and
+ // must retain distinct correctness checksums.
+ .{ .id = "warm-page-filtered", .lifecycle = .warm_page, .decoration = .highlighted, .tint = .filtered },
+ .{ .id = "warm-page-full", .lifecycle = .warm_page, .decoration = .highlighted, .tint = .full },
};
const highlights = [_]pdf.Highlight{
@@ -232,7 +250,13 @@ fn measure(
if (scenario.lifecycle == .cold_document_page) {
for (0..rounds) |round| {
- const timed = try timeCold(config.path, config.page, profile.request, scenario.decoration);
+ const timed = try timeCold(
+ config.path,
+ config.page,
+ profile.request,
+ scenario.decoration,
+ scenario.tint,
+ );
try verifyIdentity(&expected, timed.identity, profile, scenario, round);
if (round >= config.warmup) {
samples[sample_at] = timed.ns;
@@ -250,12 +274,14 @@ fn measure(
config.page,
profile.request,
scenario.decoration,
+ scenario.tint,
),
.warm_document_pass => try timeDocumentPass(
&document,
multi_pages,
profile.request,
scenario.decoration,
+ scenario.tint,
),
.cold_document_page => unreachable,
};
@@ -288,11 +314,12 @@ fn timeCold(
page: usize,
request: pdf.RenderRequest,
decoration: Decoration,
+ tint: pdf.TintMode,
) !Timed {
const started = nowNs();
var document = try pdf.Document.open(path);
errdefer document.deinit();
- const rendered = try renderPage(&document, page, request, decoration);
+ const rendered = try renderPage(&document, page, request, decoration, tint);
const elapsed = @max(@as(u64, 1), nowNs() -| started);
const identity = try consumeRender(rendered, page, .{});
document.deinit();
@@ -304,9 +331,10 @@ fn timePage(
page: usize,
request: pdf.RenderRequest,
decoration: Decoration,
+ tint: pdf.TintMode,
) !Timed {
const started = nowNs();
- const rendered = try renderPage(document, page, request, decoration);
+ const rendered = try renderPage(document, page, request, decoration, tint);
const elapsed = @max(@as(u64, 1), nowNs() -| started);
return .{
.ns = elapsed,
@@ -319,6 +347,7 @@ fn timeDocumentPass(
page_count: usize,
request: pdf.RenderRequest,
decoration: Decoration,
+ tint: pdf.TintMode,
) !Timed {
const rendered = try gpa.alloc(pdf.Render, page_count);
defer gpa.free(rendered);
@@ -328,7 +357,7 @@ fn timeDocumentPass(
errdefer for (rendered[0..initialized]) |item| gpa.free(item.rgba);
const started = nowNs();
for (rendered, 0..) |*item, page| {
- item.* = try renderPage(document, page, request, decoration);
+ item.* = try renderPage(document, page, request, decoration, tint);
initialized += 1;
}
elapsed = @max(@as(u64, 1), nowNs() -| started);
@@ -344,11 +373,18 @@ fn renderPage(
page: usize,
request: pdf.RenderRequest,
decoration: Decoration,
+ tint: pdf.TintMode,
) !pdf.Render {
- return switch (decoration) {
+ const rendered = switch (decoration) {
.plain => document.renderAt(gpa, page, request),
.highlighted => document.renderWithHighlightsAt(gpa, page, request, &highlights),
};
+ const fresh = try rendered;
+ errdefer gpa.free(fresh.rgba);
+ // Keep the original scenarios instruction-for-instruction identical.
+ // Only the appended tint scenarios enter the linear pixel pass.
+ if (tint != .disabled) try pdf.tintRgba(fresh.rgba, tint, tint_colors);
+ return fresh;
}
/// Hash every output byte after the timer stops. Reporting and cross-round
@@ -400,12 +436,20 @@ fn checkProfileResults(results: []const Result, profile: Profile) void {
const cold = results[0].identity;
const warm = results[1].identity;
const marked = results[2].identity;
+ const filtered = results[4].identity;
+ const full = results[5].identity;
if (!cold.eql(warm))
fatal("{s}: cold and warm plain renders produced different pixels", .{profile.id});
if (warm.pixels != marked.pixels or warm.rgba_bytes != marked.rgba_bytes)
fatal("{s}: highlighted render changed raster dimensions", .{profile.id});
if (warm.checksum == marked.checksum)
fatal("{s}: highlighted render did not change pixels", .{profile.id});
+ if (marked.pixels != filtered.pixels or marked.rgba_bytes != filtered.rgba_bytes or
+ marked.pixels != full.pixels or marked.rgba_bytes != full.rgba_bytes)
+ fatal("{s}: tint changed raster dimensions", .{profile.id});
+ if (marked.checksum == filtered.checksum or marked.checksum == full.checksum or
+ filtered.checksum == full.checksum)
+ fatal("{s}: tint did not change pixels", .{profile.id});
}
fn medianMpixPerSecond(result: Result) f64 {
@@ -439,8 +483,22 @@ fn reportText(
gpa,
"pardes PDF render benchmark (ReleaseFast)\n" ++
"path: {s} ({d} pages), selected page: {d}, multi-page pass: {d} pages\n" ++
- "warmup: {d}, sampled reps: {d}; cold = fresh Document (OS cache uncontrolled)\n\n",
- .{ config.path, document_pages, config.page + 1, multi_pages, config.warmup, config.reps },
+ "warmup: {d}, sampled reps: {d}; cold = fresh Document (OS cache uncontrolled)\n" ++
+ "tint colors: background=#{x:0>2}{x:0>2}{x:0>2}, foreground=#{x:0>2}{x:0>2}{x:0>2}\n\n",
+ .{
+ config.path,
+ document_pages,
+ config.page + 1,
+ multi_pages,
+ config.warmup,
+ config.reps,
+ tint_colors.background[0],
+ tint_colors.background[1],
+ tint_colors.background[2],
+ tint_colors.foreground[0],
+ tint_colors.foreground[1],
+ tint_colors.foreground[2],
+ },
) catch return;
out.appendSlice(
gpa,
@@ -501,8 +559,23 @@ fn reportJson(
gpa,
"{{\"benchmark\":\"pardes-pdf-render\",\"build\":\"ReleaseFast\"," ++
"\"path\":{s},\"document_pages\":{d},\"selected_page\":{d}," ++
- "\"multi_pages\":{d},\"warmup\":{d},\"reps\":{d},\"profiles\":[",
- .{ path_json, document_pages, config.page + 1, multi_pages, config.warmup, config.reps },
+ "\"multi_pages\":{d},\"warmup\":{d},\"reps\":{d}," ++
+ "\"tint_colors\":{{\"background\":\"{x:0>2}{x:0>2}{x:0>2}\"," ++
+ "\"foreground\":\"{x:0>2}{x:0>2}{x:0>2}\"}},\"profiles\":[",
+ .{
+ path_json,
+ document_pages,
+ config.page + 1,
+ multi_pages,
+ config.warmup,
+ config.reps,
+ tint_colors.background[0],
+ tint_colors.background[1],
+ tint_colors.background[2],
+ tint_colors.foreground[0],
+ tint_colors.foreground[1],
+ tint_colors.foreground[2],
+ },
) catch return;
for (profiles, 0..) |profile, i| {
out.print(