Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
skp_capture.hpp
Go to the documentation of this file.
1#pragma once
2
3// skp_capture.hpp — Skia `.skp` (SkPicture) frame capture.
4//
5// The capture writes a rendered frame as a Skia `.skp` artifact that
6// Skia's own `skiadebugger` can replay op-by-op. This is the bridge
7// from "a frame looked weird in Pulp" to the best-in-class external
8// frame-debugging tool — for free, because Skia ships the format.
9//
10// Design:
11//
12// * `.skp` is the legacy `SkPicture` serialization format. It is
13// fully backend-independent — an `SkPictureRecorder` records draw
14// ops into a backend-agnostic `SkRecord`, and nothing in the
15// Graphite headers references `SkPicture`. So a capture works
16// regardless of which GPU backend (Graphite/Dawn) the process runs
17// and even with no live GPU context at all. We do NOT serialize a
18// `skgpu::graphite::Recording` (that is the GPU command bundle, not
19// a `.skp`).
20//
21// * Capture re-runs the frame's draw ops into a fresh recording
22// canvas. `SkpFrameCapture` owns an `SkPictureRecorder` and exposes
23// a `pulp::canvas::Canvas` over its recording `SkCanvas`, so
24// existing view-paint code paints into the capture exactly as it
25// paints a live frame. Capture is a user-triggered action, never a
26// per-frame cost.
27//
28// * The one caveat the spike pinned: `SkPicture::serialize()` drops
29// embedded `SkImage`s to `nullptr` by default. `SkpFrameCapture`
30// always supplies `SkSerialProcs::fImageProc` (PNG-encode) so
31// atlas/image draws survive the round trip. A replayer (skiadebugger,
32// or a test) reads the artifact with the matching
33// `SkDeserialProcs::fImageProc` that decodes the PNG payload back to
34// an `SkImage`.
35//
36// * GPU-texture-backed embedded images. A raster `SkImage` PNG-encodes
37// with no GPU context. A texture-backed image (atlas / surface
38// snapshot) cannot — Skia's PNG encoder needs the owning context to
39// read the pixels off the GPU first. Pulp renders on Graphite, whose
40// pixel readback is `skgpu::graphite::Context::asyncRescaleAndReadPixels`.
41// So a capture that may embed GPU images must be handed the Graphite
42// `Context` (see `capture_skp_to_file` / `SkpFrameCapture` ctor): the
43// image proc then rasterizes texture-backed images before encoding.
44// If a texture-backed image is met with no `Context`, the proc fails
45// loudly (logged) rather than silently dropping the image.
46//
47// Graceful degradation: when Skia is not compiled in, `SkpFrameCapture`
48// reports `available() == false`, `canvas()` returns `nullptr`, and the
49// capture functions return a failed `SkpCaptureResult` with a reason
50// string — no crash, no partial file.
51
53
54#include <cstddef>
55#include <functional>
56#include <memory>
57#include <string>
58
59namespace skgpu::graphite {
60class Context;
61}
62
63namespace pulp::render {
64
69 bool ok = false;
70 std::string path;
71 std::size_t bytes_written = 0;
72 std::size_t op_count = 0;
73 std::string reason;
74
75 explicit operator bool() const { return ok; }
76};
77
93public:
103 explicit SkpFrameCapture(int width, int height,
104 skgpu::graphite::Context* graphite_context = nullptr);
106
109
113 bool available() const;
114
118
123
127 SkpCaptureResult finish_to_memory(std::string& out_blob);
128
129private:
130 struct Impl;
131 std::unique_ptr<Impl> impl_;
132};
133
145 int width, int height, const std::string& path,
146 const std::function<void(canvas::Canvas&)>& paint,
147 skgpu::graphite::Context* graphite_context = nullptr);
148
154
165 int width, int height,
166 const std::function<void(canvas::Canvas&)>& paint,
167 skgpu::graphite::Context* graphite_context = nullptr);
168
169} // namespace pulp::render
Definition canvas.hpp:240
SkpFrameCapture & operator=(const SkpFrameCapture &)=delete
SkpFrameCapture(int width, int height, skgpu::graphite::Context *graphite_context=nullptr)
Begin a capture sized to width x height logical pixels.
canvas::Canvas * canvas()
The canvas to paint the frame into.
SkpFrameCapture(const SkpFrameCapture &)=delete
SkpCaptureResult finish_to_memory(std::string &out_blob)
Finish recording and serialize the .skp into out_blob (raw .skp bytes).
bool available() const
True when Skia is compiled in and the recording canvas exists.
SkpCaptureResult finish_to_file(const std::string &path)
Finish recording and write the .skp to path.
@ Context
Definition transaction.hpp:35
Definition gpu_fwd.hpp:31
SkpCaptureResult capture_skp_to_file(int width, int height, const std::string &path, const std::function< void(canvas::Canvas &)> &paint, skgpu::graphite::Context *graphite_context=nullptr)
One-shot helper: capture a frame by handing a paint callback the capture canvas, then writing the ....
SkpCaptureResult maybe_capture_skp_from_env(int width, int height, const std::function< void(canvas::Canvas &)> &paint, skgpu::graphite::Context *graphite_context=nullptr)
Env-var-triggered one-shot .skp capture.
@ path
PathAtlas — rasterized vector paths.
Definition atlas_inventory.hpp:43
bool skp_capture_supported()
true when this build can produce .skp captures (Skia compiled in).
Definition skia_surface.hpp:16
Outcome of a .skp capture attempt.
Definition skp_capture.hpp:68
std::string reason
Failure explanation when !ok.
Definition skp_capture.hpp:73
bool ok
True when a .skp artifact was produced.
Definition skp_capture.hpp:69
std::string path
Filesystem path of the written .skp, when ok.
Definition skp_capture.hpp:70
std::size_t op_count
Approximate recorded draw-op count.
Definition skp_capture.hpp:72
std::size_t bytes_written
Size of the serialized .skp blob.
Definition skp_capture.hpp:71