The repository had no licence at all, so every source was all-rights-reserved by default. Add the verbatim MIT text at LICENSE (Copyright (c) 2026 Martino Ferrari) and a two-line SPDX header to each of the 113 first-party sources -- 48 include/plot, 26 src, 10 adapters, 5 examples, 21 tests, 3 tools -- so the grant travels with each file rather than relying on the root document alone. Scripts keep their shebang on line 1; the SPDX lines follow it. Genuinely vendored code is untouched: third_party/stb_truetype.h carries no header of ours. src/render/embedded_mono_font.cpp is generated, so tools/fonts/gen_atlas.py emit() now writes the two lines into its header comment and the file was regenerated -- gen_atlas.py --check passes and the regeneration adds only those comment lines to the previously committed file. README's provenance section now states MIT and links LICENSE. No code, test or public API change.
66 lines
2.4 KiB
C++
66 lines
2.4 KiB
C++
// SPDX-License-Identifier: MIT
|
|
// SPDX-FileCopyrightText: 2026 Martino Ferrari
|
|
|
|
#pragma once
|
|
|
|
#ifdef PLOT_RING_STATS
|
|
|
|
#include <atomic>
|
|
|
|
#include <plot/core/types.hpp>
|
|
|
|
namespace plot::render::detail {
|
|
|
|
/// Test-only instrumentation for the persistent-mapped instance ring (see
|
|
/// `InstanceStore`). Every counter exists only when PLOT_RING_STATS is defined
|
|
/// -- the test build defines it, on the library and on the suite -- and none of
|
|
/// them is a data member of any class, so a consumer compiled without the macro
|
|
/// still lays `InstanceStore` out identically. No other build carries a counter
|
|
/// or an increment on the upload, frame or wait path.
|
|
///
|
|
/// The properties they pin are invisible in the pixels a frame draws: a ring that
|
|
/// syncs mid-frame rasterises exactly what one that does not does, and a ring that
|
|
/// recycles a slot within a frame draws correctly until the GPU happens to be
|
|
/// slower than the CPU. So `InstanceStore` counts
|
|
///
|
|
/// * `ring_uploads()` -- one per upload, i.e. a frame's upload count;
|
|
/// * `ring_waits()` -- one per fence wait. At most one per store per frame:
|
|
/// the wait is at the start of the frame, on the previous frame's fence;
|
|
/// * `ring_slots()` -- the largest ring any store has grown to, which must
|
|
/// cover the uploads a frame performs.
|
|
///
|
|
/// They are process-global rather than per-store because a test that drives one
|
|
/// `InstanceStore` reads them directly; a test that drives the whole `Renderer`
|
|
/// reads the aggregate, where there are two stores and so at most two waits a
|
|
/// frame.
|
|
///
|
|
/// Not part of any query surface and not meant for production use.
|
|
inline std::atomic<u64>& ring_uploads() {
|
|
static std::atomic<u64> count{0};
|
|
return count;
|
|
}
|
|
|
|
inline std::atomic<u64>& ring_waits() {
|
|
static std::atomic<u64> count{0};
|
|
return count;
|
|
}
|
|
|
|
inline std::atomic<u64>& ring_slots() {
|
|
static std::atomic<u64> count{0};
|
|
return count;
|
|
}
|
|
|
|
/// Records `slots` as the ring's largest size so far. A compare-exchange loop
|
|
/// rather than a store because two stores report into one counter.
|
|
inline void note_ring_slots(u64 slots) noexcept {
|
|
std::atomic<u64>& count = ring_slots();
|
|
u64 largest = count.load(std::memory_order_relaxed);
|
|
while (largest < slots &&
|
|
!count.compare_exchange_weak(largest, slots, std::memory_order_relaxed)) {
|
|
}
|
|
}
|
|
|
|
} // namespace plot::render::detail
|
|
|
|
#endif // PLOT_RING_STATS
|