dart_pdf_editor_flutter_gpu 0.2.0 copy "dart_pdf_editor_flutter_gpu: ^0.2.0" to clipboard
dart_pdf_editor_flutter_gpu: ^0.2.0 copied to clipboard

Opt-in Impeller flutter_gpu tile backend for dart_pdf_editor.

dart_pdf_editor_flutter_gpu #

Opt-in retained-scene tile backend for dart_pdf_editor. It compiles supported PDF commands and uploads decoded images once per retained scene, then renders LoD tile slabs directly with Impeller's package:flutter_gpu API.

This is an experimental 0.x companion package: its public API may evolve as Flutter's GPU API matures. Unsupported content and platforms fall back to the stable Canvas renderer instead of approximating PDF output.

flutter pub add dart_pdf_editor dart_pdf_editor_flutter_gpu

This companion deliberately stays outside dart_pdf_editor's dependency graph. The core viewer keeps its Flutter 3.24 minimum and Canvas/web behavior; apps that choose this backend use Flutter 3.44 or newer and pass it explicitly:

PdfReader(
  bytes: bytes,
  // Keep one instance: its byte-budgeted image textures are reusable across
  // retained scenes, pages, workers, and LoDs.
  tileRasterBackend: FlutterGpuTileRasterBackend(
    maxTextureBytes: 256 << 20,
    maxGeometryBytes: 256 << 20,
    // Ordinary filled outlines use a scale-independent curve atlas by
    // default. Set false only for an A/B against legacy stencil fans.
    analyticText: true,
    // Optional: retain simple substituted text using the native faces that
    // Canvas normally selects. Leave false if the app registers replacements
    // under these family names.
    systemTextOutlines: true,
  ),
)

Unembedded text remains on the conservative Canvas fallback by default because Flutter does not expose the glyph paths selected by TextPainter. A host that owns its font registrations can pass a FlutterGpuTrueTypeTextOutliner whose resolver returns FlutterGpuFontFace instances built from those exact bytes, including FlutterGpuTrueTypeFontFace and FlutterGpuOpenTypeCffFontFace. systemTextOutlines: true is the native convenience adapter: it probes known Helvetica/Arial, Times, Courier, Symbol, and platform-equivalent font files. On macOS it also resolves the exact Songti, Heiti, Hiragino Sans, and Hiragino Mincho faces selected by the Canvas CJK substitution stack, including OpenType CFF faces inside font collections. It declines a run when the requested face, glyph, or simple horizontal placement cannot be proved. It is deliberately opt-in because an app may have registered different bytes under the same Flutter family name. Web keeps the Canvas path.

Flutter GPU must also be enabled by the host. Add <key>FLTEnableFlutterGPU</key><true/> to the iOS/macOS Info.plist (and FLTEnableImpeller where Impeller is not already the platform default), or this inside the Android <application> element:

<meta-data
  android:name="io.flutter.embedding.android.EnableFlutterGPU"
  android:value="true" />

For a desktop development launch, pass both engine opt-ins:

flutter run -d windows --enable-impeller --enable-flutter-gpu
flutter run -d linux --enable-impeller --enable-flutter-gpu

Packaged desktop applications must arrange the equivalent engine settings before the engine starts. The DartPDF PR preview workflow demonstrates this with profile-mode Windows/Linux bundles and also publishes a macOS DMG.

No master SDK, native-assets hook, or runtime shader compiler is required. The Metal, GLES/GLES3, and Vulkan runtime stages are compiled offline and checked in as a package asset. Unsupported platforms, disabled contexts, and unsupported PDF features return to the Canvas tile backend automatically. Web gets a compile-time stub and therefore preserves the all-platform host surface.

The current exact subset is solid paths and strokes (including zero-width PDF hairlines that stay one device pixel at every LoD), filled and stroked embedded-outline text, simple substituted text when an exact FlutterGpuTextOutliner resolves it, decoded images/image masks, Gouraud meshes, axial gradients, nested-circle radial gradients, vector and stencil-image tiling cells, normal/Multiply/Screen blending, and exact destination-sampling Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, and Luminosity blending, rectangular and arbitrary path clips, and the common isolated single-image soft-mask group. Isolated transparency groups containing a single vector fill, stroke, outlined text run, or ordinary image also stay on the GPU: their group alpha and page blend mode are retained exactly. One-element knockout groups are included because there are no sibling elements for knockout to change. Alpha-one, normal-blend non-knockout groups may contain multiple ordered fills, strokes, outlined text runs, and ordinary images because source-over is associative, so their isolation layer is an identity operation. Isolated overlapping groups retain those same paint types in the bounded offscreen tile pass before applying group alpha and the outer blend once. Normal, Multiply, and Screen remain per-paint state inside that attachment rather than being collapsed into the group's outer blend. Platform-decoded JPEGs whose /SMask remains a companion GPU surface also keep their base and mask as separate cached textures and combine them in the same shader path. A single vector fill can use one opaque grayscale image soft mask directly through retained stencil geometry. Rectangular vector soft-mask fills, including alpha or luminosity backdrops and linearized transfer functions, are partitioned into constant-mask stencil cover cells and need no intermediate texture. Advanced blend paints use bounded ping-pong tile attachments. Paints whose bounds prove they cannot affect one another share one destination-sampling pass; overlapping paints replay sequentially to preserve PDF painter order. The route rejects before allocating when its temporary attachments would exceed 256 MiB. Rectangles use hardware scissors; other clip stacks compile once into retained stencil geometry and preserve nonzero/even-odd plus save/restore semantics. The mask case keeps the base and mask as two GPU textures and combines them during tile replay; it never builds an eager full-size RGBA composite or reads pixels back to the CPU. Worker-retained RGBA uploads directly; locally platform-decoded images pay at most one readback before entering the shared texture cache. A backend-wide byte-budgeted LRU preserves decoded texture identity across scenes, pages, workers, zoom levels, and LoDs. Pinned scene textures count toward the same ceiling; if no unpinned entry can make room, that scene falls back to Canvas instead of overshooting the budget. Compiled vertices share a second strict byte budget. They are packed into reusable 16 MiB device-buffer blocks and returned to the backend-wide pool only after their scene is disposed and every submitted command buffer completes. This keeps command-heavy CAD navigation bounded without relying on delayed native finalizers; a scene that cannot lease enough geometry also falls back.

Ordinary filled outline text uses retained Slug-style quadratic curve streams: one small nearest-sampled atlas stores each distinct page glyph and each draw retains only six vertices per placed glyph. The fragment shader derives its pixels-per-em from the current tile transform, so the same atlas stays sharp across LoDs and rotated text. Gradient and soft-masked text, malformed or over-complex outlines, and an atlas over the bounded 8 MiB ceiling retain the existing stencil-fan path. Atlas creation failure is likewise an optimization fallback, never a reason to reject the page.

After useful page pixels land and foreground work stays quiet for 750 ms, the viewer asks the backend to warm its context. The backend submits one transparent pixel through each tile pipeline and the nonzero stencil-cover state used by retained fills, moving Impeller/driver compilation out of the first deep-zoom interaction without delaying initial document paint or competing with an immediate scroll. This work is coalesced per native view and MSAA mode, even when several readers share the same process.

Proactive warm-up defaults on for macOS, Windows, and Linux. Android and iOS stay on-demand by default because merely creating an Impeller GPU context can reserve significant memory before a page is known to be GPU-compatible. A host that has validated its mobile device range can opt in with enableProactiveWarmUp: true; normal on-demand GPU tiles remain available when the option is false.

The same idle gate then prepares the live page's retained tile session: scene geometry and decoded-image uploads are compiled once and every scene pipeline is submitted at one-pixel page scale. The real first tile reuses those retained resources. Starting new foreground work cancels and restarts both delays; page disposal releases the prepared resources through the ordinary scene lifecycle.

backend.stats reports accepted/rejected/active sessions, the latest actual tile route, runtime fallback reasons, context and scene warm-up outcomes, scene compile and tile-submit time, spatially selected command counts, upload/readback paths, cache hits and evictions, budget fallbacks, retained bytes, and live resource leases. Clip diagnostics separately report paths compiled and tile-mask rebuilds. Advanced-blend diagnostics report destination-sampling passes, allocated and peak temporary bytes, and budget fallbacks. backend.stats.toJson() is suitable for benchmark artifacts. Keep the backend instance alive when comparing pages so those counters and cross-page caches describe the real workload rather than one page at a time.

Pages with other transparency groups or soft masks, scenes that combine advanced blend paints with offscreen transparency groups, non-nested radial gradients, gradient overprint, unsafe overprint, complex clips nested inside the single-image soft-mask shortcut, unresolved substituted text, or missing image pixels are rejected as a whole rather than approximated. allowOverprintApproximation exists only for controlled experiments and defaults to false.

Persistent LoD tiles #

dart_pdf_editor can also persist accepted or Canvas-fallback tiles through the existing platform-neutral cache seam. Use a separate byte budget so tiles cannot evict cold-navigation previews:

final store = createPersistentCacheStore();
final rasterCache = PdfRasterCache(
  PdfDiskCache(store, namespace: 'page-previews'),
  tiles: PdfDiskCache(
    store,
    namespace: 'lod-tiles',
    maxBytes: 256 << 20,
  ),
);

PdfReader(
  bytes: bytes,
  rasterCache: rasterCache,
  tileRasterBackend: gpuBackend,
)

A disk lookup races the ordinary tile render, so a cold or slow store cannot delay first content. A hit may win the race and populate the memory LRU; a fresh tile is admitted to the compositor before its PNG encoding and disk write begin. Cache keys include the document, revision/page stamps, page visual options, tile coordinate, exact region, dimensions, and LoD ratio.

Validation and measurement #

Run native tests with the opt-in flags:

fvm flutter test --enable-impeller --enable-flutter-gpu

test/gpu_corpus_test.dart checks every Ghent file and the PDF.js corpus, comparing every GPU-accepted page against Canvas while treating conservative rejection as the intended fallback. test/real_document_benchmark_test.dart measures scene recording/image decode, cold and warm 512 px LoDs, forced visual settle, Canvas parity, upload/cache counters, and RSS. It is opt-in via PDF_GPU_BENCHMARK_PDF and accepts a zero-based comma-separated page list in PDF_GPU_BENCHMARK_PAGES. Set PDF_GPU_BENCHMARK_OUT to a directory to save the center 512 px GPU and Canvas tiles for visual comparison, or PDF_GPU_BENCHMARK_MSAA=0 to isolate multisample antialiasing differences. Set PDF_GPU_BENCHMARK_WARMUP=1 to measure the viewer's pipeline warm-up before the first real tile. Set PDF_GPU_BENCHMARK_SCENE_WARMUP=1 to additionally compile and submit the retained scene before measuring that tile. Set PDF_GPU_BENCHMARK_SCENARIO to emit normalized PdfPerfLog scenario markers for each pipeline/scene/tile phase. CI uses those markers to run the checked-in tiling-pattern, radial-shading, hairline, advanced-blend, vector-mask transfer, PDF.js knockout soft-mask and isolated-knockout overlap, GWG168/169 vector soft-mask, and GWG1610/1611 text soft-mask pages plus the PDF.js Latin and CJK system-font outline pages and deterministic deferred-mask fixture six times on macOS Metal, compare the exact PR base and candidate on the same runner with balanced execution order, and publish both a concise PR headline and a collapsed detailed trace. Set PDF_GPU_BENCHMARK_FIXTURE=deferred-mask instead of a PDF path to exercise a deterministic 1024x768 JPEG under a Flate grayscale soft mask. This fixture emits the same first-tile and Canvas scenarios whether the backend accepts the scene or production falls back to Canvas, so a backend-routing change remains a like-for-like comparison. Pipeline and scene warm-up scenario markers are suppressed for this production-route fixture; the warm-up itself still runs. An unmeasured Canvas pass immediately before the Canvas control keeps that reference warm on both the accepted and fallback routes without warming the cold first-tile measurement. Set PDF_GPU_BENCHMARK_ROUTE_CHANGE=1 for the same normalization when a PDF path, rather than the built-in fixture, changes from Canvas fallback to direct GPU. Route-change scene warm-up still runs when requested, matching the viewer's idle-prepared path, but its unmatched timing marker is omitted because the Canvas base has no GPU session to warm. CI uses it for the checked-in vector-mask transfer, knockout soft-mask, isolated-knockout overlap, hairline, advanced-blend, and GWG vector/text soft-mask pages. Set PDF_GPU_BENCHMARK_SYSTEM_TEXT=1 to enable the native system-font outline adapter. For a like-for-like macOS parity control, PDF_GPU_BENCHMARK_REGISTER_SYSTEM_FONTS=1 registers those same font bytes under Canvas's substitution-family names; CI combines both settings for the system-font and advanced-blend route-change scenarios. The corpus equivalents are GPU_CORPUS_SYSTEM_TEXT=1 and GPU_CORPUS_REGISTER_SYSTEM_FONTS=1; the designated macOS GPU lane runs that full parity matrix on every change. Set PDF_GPU_BENCHMARK_ANALYTIC_TEXT=0 or GPU_CORPUS_ANALYTIC_TEXT=0 for a same-build comparison against the retained stencil-fan text path. Set PDF_GPU_BENCHMARK_OVERPRINT=0 to exercise the production-default exact fallback policy; the benchmark otherwise enables its documented source-over approximation so more of a corpus can be measured on the GPU.

0
likes
0
points
661
downloads

Publisher

unverified uploader

Weekly Downloads

Opt-in Impeller flutter_gpu tile backend for dart_pdf_editor.

Repository (GitHub)
View/report issues

Topics

#pdf #pdf-rendering #flutter #gpu #impeller

License

unknown (license)

Dependencies

dart_pdf_editor, flutter, flutter_gpu, pdf_cos, pdf_document, pdf_graphics, vector_math

More

Packages that depend on dart_pdf_editor_flutter_gpu