Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
host_drag_coalescer.hpp
Go to the documentation of this file.
1// host_drag_coalescer.hpp — the host-side half of per-frame drag coalescing.
2//
3// `PointerCoalescer` collapses a STREAM and deliberately knows nothing about
4// hosts. Every host that uses it still has to get the same four things right:
5//
6// 1. a fail-safe, so motion is never held when nothing will release it,
7// 2. one dirty signal per HELD RUN rather than one per sample — the point of
8// coalescing is removing O(events) work, and an invalidation per sample
9// is exactly that work,
10// 3. flush-before-handoff ordering at every path that ends a gesture, and
11// 4. the counters that make "is coalescing actually on?" answerable, because
12// the fail-safe means a host that silently loses it looks *correct* and
13// merely runs at the old frame rate,
14// 5. and the one rule that is NOT obvious: a RELATIVE motion delta must be
15// SUMMED across merged samples, never replaced by the last one. Keeping
16// only the survivor's delta silently shortens every relative drag in
17// proportion to how many samples were merged, which reads as "the knob
18// feels sluggish" and not as a dropped-input bug. Absolute position needs
19// no such care — the newest one is already the whole displacement — which
20// is exactly why the distinction is easy to miss.
21//
22// Those were written once, inline, in the standalone macOS window host. The
23// macOS PLUG-IN editor host — the surface a musician actually drags in a DAW —
24// got none of them, because it is a different ObjC class. That is what this
25// type exists to stop: the rules live in one portable, headlessly testable
26// place, and a host supplies only delivery.
27//
28// Diagnostics are not decoration here. (4) is the whole reason `stats()` is
29// public: the standalone host lost coalescing once to a frame driver that
30// never opted in, and because the fail-safe restored the old behaviour there
31// was no crash, no log and no red test — only a measured 4x that reproduced on
32// some runs and not others.
33
34#pragma once
35
37
38#include <cstdint>
39#include <functional>
40
41namespace pulp::view {
42
49 public:
53 using Deliver = std::function<void(const PointerSample&)>;
54
56 struct SubmitResult {
62 bool arm_repaint = false;
66 std::size_t delivered = 0;
67 };
68
69 struct Stats {
72 std::uint64_t raw_samples = 0;
74 std::uint64_t delivered_samples = 0;
76 std::uint64_t flushes = 0;
79 std::uint64_t merged_samples = 0;
80 };
81
90 void set_frame_driver_running(bool running, const Deliver& deliver);
91
92 bool frame_driver_running() const {
93 return frame_driver_running_;
94 }
95
97 SubmitResult submit(const PointerSample& sample, const Deliver& deliver);
98
103 std::size_t flush_frame(const Deliver& deliver);
104
107 void discard();
108
109 bool has_pending() const {
110 return coalescer_.has_pending();
111 }
112
113 const Stats& stats() const {
114 return stats_;
115 }
116
117 private:
118 std::size_t deliver_all_(std::vector<PointerSample>& samples, const Deliver& deliver);
123 void apply_accumulated_movement_(std::vector<PointerSample>& samples);
124 void accumulate_movement_(const PointerSample& sample);
125
126 PointerCoalescer coalescer_;
127 Stats stats_{};
128 bool frame_driver_running_ = false;
129 float pending_movement_x_ = 0.0f;
130 float pending_movement_y_ = 0.0f;
131 bool pending_has_movement_ = false;
132};
133
134} // namespace pulp::view
Owns a PointerCoalescer plus the host-side policy around it: the frame-driver opt-in,...
Definition host_drag_coalescer.hpp:48
const Stats & stats() const
Definition host_drag_coalescer.hpp:113
void discard()
Drop held motion WITHOUT delivering it, for a host tearing down mid-gesture where the target may alre...
void set_frame_driver_running(bool running, const Deliver &deliver)
Opt in only from a host that has committed to calling flush_frame once per presented frame.
bool frame_driver_running() const
Definition host_drag_coalescer.hpp:92
std::size_t flush_frame(const Deliver &deliver)
Release motion held since the last presented frame.
bool has_pending() const
Definition host_drag_coalescer.hpp:109
std::function< void(const PointerSample &)> Deliver
Delivers one sample to the view tree.
Definition host_drag_coalescer.hpp:53
SubmitResult submit(const PointerSample &sample, const Deliver &deliver)
Feed one raw pointer sample.
Collapses runs of motion into one sample per presented frame while leaving button transitions untouch...
Definition pointer_coalescer.hpp:71
Hover-cursor tracking for platform window hosts.
Definition fwd.hpp:27
Definition host_drag_coalescer.hpp:69
std::uint64_t merged_samples
Samples merged away and never dispatched.
Definition host_drag_coalescer.hpp:79
std::uint64_t delivered_samples
Samples that actually reached the view tree.
Definition host_drag_coalescer.hpp:74
std::uint64_t raw_samples
Samples the host submitted — what an uncoalesced host would have dispatched.
Definition host_drag_coalescer.hpp:72
std::uint64_t flushes
flush_frame calls, i.e. presented frames seen.
Definition host_drag_coalescer.hpp:76
What a host must do in response to a submitted sample.
Definition host_drag_coalescer.hpp:56
std::size_t delivered
Samples handed to deliver during this call.
Definition host_drag_coalescer.hpp:66
bool arm_repaint
The sample was held and this is the idle->pending edge.
Definition host_drag_coalescer.hpp:62
One pointer sample as the host received it, before any coalescing.
Definition pointer_coalescer.hpp:46