cover_buckets chose one level per pass, so an odd first whole bucket (half of all
measurement ranges) never reached a level above 0 and folded every remaining
bucket at level 0 -- 15624 entries for a 1e6-sample pyramid. Pick the largest
aligned, in-range, materialised level at each cursor instead; the new cost test
counts folded entries through a PLOT_PYRAMID_COUNT_COVERS-only hook (absent from
normal builds) and fails on the old body.
Ring slots are now relaxed std::atomic: overwrite-on-full means the producer
reuses the slot of a snapshot's oldest sample while the consumer reads it, which
was a data race, not just a stale value. copy_from loads explicitly instead of
std::copy; push stays lock-free. A new producer/consumer case is the one TSan
flags without this.
Also extends the wrapped-window test with x0/bounds/pyramid/sum/sumsq/gather
checks, and corrects the StreamSeries threading note to the sample-count bound.
- loader: add available(), true only after load() has resolved every
required entry point (false at the start of load()). Program::from_source
and the other fallible constructors return ErrorCode::Unsupported while it
is false, so a failed load() with a partially populated table can no longer
fault on an unresolved entry point.
- caps: GL_CONTEXT_PROFILE_MASK only exists from 3.2, so query it only on
3.2+ contexts. A 3.0/3.1 query latches GL_INVALID_ENUM in the context, which
the next unrelated glGetError() (e.g. read_pixel) then reports as failure.
- tests: gate the dlsym/libGL.so.1 fallback assertions on libGL being
resolvable, so the GL suite also passes on a GLES-only host.