Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
b1dc6fb
Document stacking implementation plan
imakris Jul 10, 2026
3fe78e6
style: align switch case layout
imakris Jul 10, 2026
c7e82d9
Record staged style baseline remediation
imakris Jul 10, 2026
36947c3
style: split else-if layout
imakris Jul 10, 2026
4b950f8
style: normalize guard tables
imakris Jul 10, 2026
1034d1b
style: align test macro continuations
imakris Jul 10, 2026
d76a403
style: wrap long signatures
imakris Jul 10, 2026
9aeec20
style: normalize prototype blocks
imakris Jul 10, 2026
7ac9a99
style: normalize constructor initializer layout
imakris Jul 10, 2026
690aa2c
style: normalize aggregate brace layout
imakris Jul 10, 2026
4a2280e
style: align struct member declarations
imakris Jul 10, 2026
fa00af2
style: align using declarations
imakris Jul 10, 2026
7bf094b
style: align local declarations
imakris Jul 10, 2026
a4b4f8d
style: align assignment tables
imakris Jul 10, 2026
03b435a
style: align inline method tables
imakris Jul 10, 2026
ccb6cb8
style: align call argument tables
imakris Jul 10, 2026
2ac6723
style: wrap long calls
imakris Jul 10, 2026
b5d9213
style: wrap long conditions
imakris Jul 10, 2026
3bc7cb1
style: normalize ternary continuations
imakris Jul 10, 2026
e275e2c
docs: close baseline dependency gate
imakris Jul 10, 2026
f90dee1
docs: isolate style token repairs
imakris Jul 10, 2026
69c94af
style: avoid ambiguous shader helper ternary
imakris Jul 10, 2026
df10722
style: normalize hanging indent
imakris Jul 10, 2026
06d49df
style: wrap expression right-hand sides
imakris Jul 10, 2026
d84151a
docs: extend style execution ledger
imakris Jul 10, 2026
b2a5979
style: split long test message
imakris Jul 10, 2026
c625698
style: rename private uniform padding fields
imakris Jul 10, 2026
60ddf4f
style: reduce hard-limit source lines
imakris Jul 10, 2026
d0b7adf
docs: govern formatter conflict workarounds
imakris Jul 10, 2026
2ec8013
style: resolve formatter boundary cases
imakris Jul 10, 2026
cd59a8d
docs: record final style remediation cycle
imakris Jul 10, 2026
dd5a14d
docs: repin repaired style pipeline
imakris Jul 10, 2026
6e470ed
benchmark: stabilize streaming revisions
imakris Jul 10, 2026
f7bedb4
benchmark: retain hot-path baseline evidence
imakris Jul 10, 2026
bc167da
benchmark: harden calibration provenance
imakris Jul 10, 2026
715d6b8
benchmark: close checkpoint evidence gaps
imakris Jul 10, 2026
0dbbb41
benchmark: make checkpoint evidence reviewable
imakris Jul 10, 2026
2aa61d0
merge: retain implementation plan ancestry
imakris Jul 10, 2026
049587c
docs: align checkpoint governance with owner decisions
imakris Jul 10, 2026
81da254
benchmark: harden retained checkpoint evidence
imakris Jul 10, 2026
fea5a84
benchmark: fingerprint renderer execution settings
imakris Jul 10, 2026
86ad4c0
benchmark: isolate live measurement schedule
imakris Jul 10, 2026
f07549b
ci: retain Unix renderer failures
imakris Jul 10, 2026
c49d43f
benchmark: bind smoke renderer provenance
imakris Jul 10, 2026
906b652
ci: preserve shader-capable native smoke
imakris Jul 10, 2026
fc86d19
ci: trust the exact FreeBSD checkout
imakris Jul 10, 2026
c5d6265
rhi: retain a desktop GLSL 330 path
imakris Jul 10, 2026
35c2f83
docs: describe both desktop GLSL profiles
imakris Jul 10, 2026
273e90e
benchmark: enforce fixed render calibration contract
imakris Jul 10, 2026
a83316e
ci: use the available FreeBSD Mesa renderer
imakris Jul 10, 2026
836b178
ci: retain FreeBSD backend allocation evidence
imakris Jul 10, 2026
989f5af
benchmark: avoid duplicate OpenGL context probing
imakris Jul 10, 2026
aad57ed
docs: clarify fallback context lifetime
imakris Jul 10, 2026
6e815dd
benchmark: normalize POSIX aligned allocations
imakris Jul 10, 2026
26a4cc6
benchmark: finalize checkpoint evidence remediation
imakris Jul 10, 2026
b7ef9a6
benchmark: recover pre-calibration gate
imakris Jul 11, 2026
b26edf5
benchmark: remove Git identity calibration gates
imakris Jul 11, 2026
c17b645
cleanup: remove generated process machinery
imakris Jul 11, 2026
71d1686
plot: add stacked series composition
imakris Jul 11, 2026
16c52cf
benchmark: exercise stacked area rendering
imakris Jul 11, 2026
a539bfa
example: expose stacked function plotting
imakris Jul 11, 2026
703aa0f
test: expose function plotter check to CTest
imakris Jul 11, 2026
49e5281
style: apply canonical formatting
imakris Jul 11, 2026
fb0bdf4
example: reorder stacked functions by drag
imakris Jul 11, 2026
14ac654
fix: use native spinbox stepping
imakris Jul 11, 2026
2c3b16d
fix: clarify stacked data indicator
imakris Jul 11, 2026
24d2cdc
fix: gate indicator sum on rendered stack
imakris Jul 11, 2026
c84bd9f
fix: align stacked indicator markers
imakris Jul 11, 2026
6cd7785
fix: hide unresolved stack markers
imakris Jul 11, 2026
086f787
fix: preserve singleton stack marker
imakris Jul 11, 2026
fa8a531
fix: fit and wrap stacked plot UI
imakris Jul 11, 2026
3bbd881
fix: match stack fit renderer eligibility
imakris Jul 11, 2026
e2379fa
plot: highlight stacked totals
imakris Jul 11, 2026
d65a060
docs: explain stacked series API
imakris Jul 11, 2026
7d5a1c9
fix: preserve nanosecond timestamp precision
imakris Jul 11, 2026
ee30f91
perf: reuse unchanged line geometry
imakris Jul 11, 2026
c3fdd75
feat: expose stack composition status
imakris Jul 11, 2026
8c12875
perf: bound stacked series composition
imakris Jul 11, 2026
d80cc2c
docs: clarify stack composition contract
imakris Jul 11, 2026
a5bb6fe
perf: stream stacked source samples
imakris Jul 11, 2026
87531ca
perf: bound stacked source reads
imakris Jul 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions .github/workflows/ci-freebsd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ jobs:
include:
- name: QRhi (FreeBSD)
text: OFF
benchmark: OFF
examples: OFF
- name: QRhi+Text (FreeBSD)
text: ON
benchmark: ON
examples: ON
steps:
- name: Checkout repository
Expand All @@ -28,24 +30,30 @@ jobs:
uses: vmactions/freebsd-vm@v1
env:
TEXT: ${{ matrix.text }}
BENCHMARK: ${{ matrix.benchmark }}
EXAMPLES: ${{ matrix.examples }}
with:
release: "15.0"
usesh: true
envs: 'TEXT EXAMPLES'
envs: 'TEXT BENCHMARK EXAMPLES'
prepare: |
pkg bootstrap -fy
mkdir -p /usr/local/etc/pkg/repos
echo 'FreeBSD-ports: { enabled: no }' > /usr/local/etc/pkg/repos/FreeBSD-ports.conf
echo 'FreeBSD: { url: https://pkg.FreeBSD.org/${ABI}/latest }' > /usr/local/etc/pkg/repos/FreeBSD.conf
pkg update -f
pkg install -y git cmake pkgconf qt6-base qt6-declarative qt6-shadertools
pkg install -y git cmake pkgconf python311 qt6-base qt6-declarative qt6-shadertools xauth xorg-vfbserver
run: |
set -e
mkdir -p build
cmake -S . -B build \
-DCMAKE_BUILD_TYPE=Release \
-DVNM_PLOT_ENABLE_TEXT=${TEXT} \
-DVNM_PLOT_BUILD_TESTS=ON \
-DVNM_PLOT_BUILD_BENCHMARK=${BENCHMARK} \
-DVNM_PLOT_BUILD_EXAMPLES=${EXAMPLES} \
-DVNM_PLOT_BUILD_FUNCTION_PLOTTER=OFF
cmake --build build --config Release
cd build && env QT_QPA_PLATFORM=offscreen ctest --output-on-failure
Xvfb :99 -screen 0 1280x1024x24 >/tmp/vnm-plot-xvfb.log 2>&1 &
export DISPLAY=:99
(cd build && ctest --output-on-failure)
9 changes: 6 additions & 3 deletions .github/workflows/ci-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,14 @@ defaults:

env:
CMAKE_BUILD_TYPE: Release
# Hosted Xvfb uses Mesa's CPU renderer. Bound its worker count to the job
# memory limit and use its GL 3.3 rasterizer without selecting a QRhi API.
GALLIUM_DRIVER: softpipe
LP_NUM_THREADS: "1"
LINUX_SYSTEM_DEPS: >-
libgl-dev libx11-dev libxrandr-dev libxinerama-dev libxcursor-dev
libxi-dev libxext-dev libwayland-dev libxkbcommon-dev libvulkan-dev
xvfb xauth

jobs:
build-rhi:
Expand Down Expand Up @@ -65,9 +70,7 @@ jobs:

- name: Run tests
working-directory: build
env:
QT_QPA_PLATFORM: offscreen
run: ctest --output-on-failure
run: xvfb-run -a ctest --output-on-failure

- name: Build summary
run: |
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/ci-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@ jobs:

- name: Run tests
working-directory: build
env:
QT_QPA_PLATFORM: offscreen
run: ctest --output-on-failure -C ${CMAKE_BUILD_TYPE}

- name: Build summary
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

# Benchmark output files
inspector_benchmark_*.txt
inspector_benchmark_*.json
benchmark_phase_trace.jsonl

# QtCreator user settings
/examples/**/.qtcreator/
Expand All @@ -20,6 +22,7 @@ inspector_benchmark_*.txt
# Claude workspace metadata
.claude/
/repo/
__pycache__/

# Windows artifacts
/nul
16 changes: 7 additions & 9 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ option(VNM_PLOT_BUILD_FUNCTION_PLOTTER "Build the Qt Multimedia function_plotter
option(VNM_PLOT_ENABLE_TEXT "Enable MSDF text rendering (requires FreeType + msdfgen)" ON)
option(VNM_PLOT_BUILD_TESTS "Build test suite" OFF)
option(VNM_PLOT_USE_SYSTEM_LIBS "Prefer find_package dependencies over FetchContent when available" OFF)
set(VNM_PLOT_VNM_MSDF_TEXT_VERSION 0.2.0)

include(FetchContent)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/EmbedAssets.cmake)
Expand Down Expand Up @@ -169,7 +168,7 @@ if(_vnm_plot_missing_msdf_text_targets)
_vnm_plot_has_any_msdf_text_target(_vnm_plot_has_partial_msdf_text_targets)
if(VNM_PLOT_USE_SYSTEM_LIBS AND
NOT _vnm_plot_has_partial_msdf_text_targets)
find_package(vnm_msdf_text ${VNM_PLOT_VNM_MSDF_TEXT_VERSION} CONFIG QUIET
find_package(vnm_msdf_text CONFIG QUIET
COMPONENTS ${_vnm_plot_msdf_text_components})
_vnm_plot_alias_existing_msdf_text_targets()
_vnm_plot_missing_targets(
Expand Down Expand Up @@ -492,8 +491,8 @@ message(STATUS "vnm_plot: Building RHI library")
#
# Bake every shader in shaders/qsb/ into a multi-profile .qsb artifact and
# embed it under :/vnm_plot/shaders/qsb/<name>.qsb. Profile set covers Vulkan
# (SPIR-V), Metal 1.2 + 2.1, D3D11 (HLSL 5.0), and desktop GL 4.10 so RHI can
# pick a working artifact on every supported backend.
# (SPIR-V), Metal 1.2 + 2.1, D3D11 (HLSL 5.0), and desktop GL 3.30 + 4.10 so
# RHI can pick a working artifact on every supported backend.
# -----------------------------------------------------------------------------

set(VNM_PLOT_QSB_SHADERS
Expand All @@ -517,10 +516,9 @@ if(VNM_PLOT_ENABLE_TEXT)
endif()

# Each input shader yields :/vnm_plot/shaders/qsb/<name>.<ext>.qsb.
# GLSL profile is desktop 410 only; mobile GL ES is out of scope for
# the RHI shaders. They use storage buffers that are unavailable below
# ES 3.1, so emitting a 300 ES artifact would compile-fail at runtime
# on a real ES 3.0 driver.
# The RHI shaders use vertex attributes and std140 uniform blocks, so retain
# desktop GLSL 330 for GL 3.3 contexts and GLSL 410 for existing GL 4 paths.
# Mobile GL ES remains out of scope for this desktop renderer.
foreach(_vnm_plot_qsb_shader IN LISTS VNM_PLOT_QSB_SHADERS)
get_filename_component(_vnm_plot_qsb_output_dir
"${CMAKE_CURRENT_BINARY_DIR}/.qsb/${_vnm_plot_qsb_shader}"
Expand All @@ -533,7 +531,7 @@ unset(_vnm_plot_qsb_shader)
qt_add_shaders(vnm_plot_rhi "vnm_plot_qsb_shaders"
PREFIX "/vnm_plot"
BASE "${CMAKE_CURRENT_SOURCE_DIR}"
GLSL "410"
GLSL "330,410"
HLSL "50"
MSL "12,21"
OUTPUT_TARGETS VNM_PLOT_RHI_RESOURCE_TARGETS
Expand Down
84 changes: 83 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,86 @@ auto series = vnm::plot::Series_builder()
plot_widget->add_series(0, series);
```

### Stacked series

Give two or more ordinary series the same non-zero stack group. Group `0`
keeps a series independent.

```cpp
auto f_source = std::make_shared<vnm::plot::Vector_data_source<sample_t>>();
f_source->set_data(std::vector<sample_t>{{0, 1.0f}, {1'000'000'000, 2.0f}});

auto g_source = std::make_shared<vnm::plot::Vector_data_source<sample_t>>();
g_source->set_data(std::vector<sample_t>{{0, 3.0f}, {1'000'000'000, 4.0f}});

const auto access = vnm::plot::make_access_policy<sample_t>(
&sample_t::t_ns,
&sample_t::value);

auto f = vnm::plot::Series_builder()
.style(vnm::plot::Display_style::AREA)
.stack_group(1)
.data_source(f_source)
.access(access)
.build_shared();

auto g = vnm::plot::Series_builder()
.style(vnm::plot::Display_style::AREA)
.stack_group(1)
.data_source(g_source)
.access(access)
.build_shared();

plot_widget->add_series(10, f); // bottom
plot_widget->add_series(20, g); // top; cumulative value is f + g
```

Series are stacked bottom-to-top in ascending plot-ID order. Reordering a
stack therefore also means updating the caller's plot-ID mapping and series
registrations. `LINE` and `DOTS` draw cumulative values; `AREA` fills each
component's band between its cumulative base and top. Preview sources and
styles are composed the same way in the preview view.

Each source retains its independently selected LOD and timestamp grid. The
renderer uses their exact timestamp union when it fits the view's composition
budget. Larger unions are interpolated onto a shared deterministic grid that
targets half-pixel spacing, clipped to the common selected-source time domain.
The grid retains both domain endpoints and is capped so one stack view never
materializes more than 1,048,576 cumulative samples across all layers; very
large layer counts therefore use a coarser grid. Main and preview use their own
horizontal pixel widths and budgets. Members must use
matching `Series_interpolation` modes and, after sample and nonfinite-policy
handling, produce a single continuous drawable selected window with monotonic
timestamps. A rejected group fails closed: the affected stack view is omitted
instead of drawing misleading unstacked data, and its reason is queryable via
`stack_status` or `get_stack_status`. When configured, the optional
`Plot_config::log_error` callback may also receive a human-readable message.
Bounded resampling is an accepted `ACTIVE` result, not a rejection. Features
narrower than half a pixel may be omitted, and a `STEP_AFTER` transition may
move to the next grid timestamp; indicators and auto-fit continue to match the
geometry actually rendered.

Stack acceptance is also queryable without installing a log callback. C++ can
call `Plot_widget::stack_status(group, Series_view_kind::MAIN)` (or `PREVIEW`)
and inspect its typed `state`, `reason`, and `affected_series_ids`. `PENDING`
means no renderer result matches the current data and range, `ACTIVE` means the
view was accepted, and `SUPPRESSED` includes the specific rejection reason. A
singleton group is `ACTIVE` with no rejection because no composition is needed.
QML can call `get_stack_status(group, preview)`; the returned map contains
`group`, uppercase `view`, `state`, and `reason` strings, plus an
`affected_series_ids` list.

The final cumulative value gets an automatic line overlay two pixels thicker
than `Plot_config::line_width_px`: `#E6DFCC` in dark mode and `#192033` in light
mode. This applies to the main and preview views for `LINE`, `DOTS`, and `AREA`
stacks.

`PlotIndicator` keeps component text values raw while placing their markers on
the cumulative rendered layers. It adds a text-only `Σ` total and the note
"Markers show cumulative stack positions". `Plot_widget::auto_adjust_view()`
(used by the function plotter's **Fit Vertical** button) fits current cumulative
stack geometry, including `AREA` bases.

**Thread Safety**
`Plot_widget` renders on a separate RHI render thread. Treat `series_data_t` as immutable once added. To change series config (style, access policy, preview config, color), update a copy and call `add_series` again with the same id to replace it. Make sure your `Data_source` implementation is safe to read from the render thread.

Expand Down Expand Up @@ -188,11 +268,13 @@ cmake --build build

- `vnm_plot_hello` - renders a sine wave using the example function-source helper
- `vnm_plot_preview_config` - preview uses a separate data source and AREA style via `preview_config`
- `function_plotter` - multiple functions, per-series styles, expression evaluation via mexce
- `function_plotter` - expressions, per-series styles, and a stacked-series demo via mexce

`function_plotter` is opt-in because it depends on Qt Multimedia and `mexce`.
Build it with `-DVNM_PLOT_BUILD_EXAMPLES=ON -DVNM_PLOT_BUILD_FUNCTION_PLOTTER=ON`;
point at a local mexce checkout with `-DMEXCE_LOCAL_PATH=...`.
Use its **Σ** control to stack functions, drag a function's color accent or
label to change bottom-to-top order, and use **Fit Vertical** to fit the stack.

## Configuration

Expand Down
25 changes: 18 additions & 7 deletions benchmark/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,18 @@ profiling report format for reproducible comparisons.
## Major Components
- Ring_buffer<T>
- Thread-safe circular buffer with overwrite semantics.
- Supports copy-on-snapshot via copy_to() for stable reads.
- Exposes split-span zero-copy views protected by a shared lock.
- Keeps `copy_to()` only as an explicit copying helper; snapshots do not use it.

- Brownian_generator
- Generates Bar_sample or Trade_sample using geometric Brownian motion.
- Deterministic when seeded; timestamps advance at the configured rate.

- Benchmark_data_source<T>
- Implements vnm::plot::Data_source.
- Uses copy-on-snapshot to provide contiguous data for rendering.
- Tracks sequence numbers and computes value ranges for auto-scaling.
- Publishes zero-copy snapshots that may contain two ring-buffer spans.
- Reports logical view bytes separately from exact copied bytes (zero).
- Tracks the stable ring revision used by `current_sequence()`.

- Benchmark_profiler
- Implements vnm::plot::Profiler.
Expand All @@ -41,11 +43,14 @@ profiling report format for reproducible comparisons.
- Generator thread:
- Produces samples at the configured rate using steady_clock pacing.
- Writes into the ring buffer only; no rendering work.
- A coordinated publication barrier bounds complete logical publications
across every series to the measurement interval.

- UI/render thread:
- Executes paintGL() via Qt's event loop.
- Requests snapshots from the Data_source and renders with vnm_plot.
- Records profiling scopes through Plot_config::profiler.
- The offscreen path drives QRhi frames directly.
- Requests zero-copy snapshots from the Data_source and renders with vnm_plot.
- Records profiling scopes, render-thread CPU allocations, and QRhi buffer
allocations through Plot_config::profiler.

## Rendering Pipeline
- vnm_plot Asset_loader loads embedded shaders.
Expand Down Expand Up @@ -81,7 +86,12 @@ Benchmark_profiler produces a stable benchmark report:

The report is written at the end of the benchmark run to the configured output
folder with the inspector_benchmark_YYYYMMDD_HHMMSS_<STREAM>_<DataType>.txt
naming convention.
naming convention. Windows file operations adapt logical paths to the native
extended-length namespace without exposing that transport spelling in reports.

The phase trace flushes a fixed eleven aggregate success boundaries outside the
measured frame loop. Only a failure adds a frame-specific boundary, so external
termination retains progress without per-frame trace I/O.

## Configuration and CLI
The executable accepts CLI options for duration, data type, seed, rate, ring
Expand All @@ -93,6 +103,7 @@ resolved once at startup to keep runs reproducible.
- benchmark/include/brownian_generator.h
- benchmark/include/benchmark_data_source.h
- benchmark/include/benchmark_profiler.h
- benchmark/include/path_io.h
- benchmark/include/benchmark_window.h
- benchmark/src/benchmark_window.cpp
- benchmark/src/main.cpp
Loading
Loading