Skip to content

BUG: Fix OOC Review Findings - #1764

Open
jmarquisbq wants to merge 109 commits into
BlueQuartzSoftware:developfrom
jmarquisbq:fix/ooc-review-findings
Open

jmarquisbq wants to merge 109 commits into
BlueQuartzSoftware:developfrom
jmarquisbq:fix/ooc-review-findings

Conversation

@jmarquisbq

@jmarquisbq jmarquisbq commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Correct OOC storage contracts, selected imports, pipeline completion, and filter behavior. Add bounded array reads and working buffers, preserve geometry references, and check results against independent expectations. 197 files changed, +32,125 / −1,657 lines.

Companion PRs:

Publication status, October 23: The tested rebase onto current develop is published. All branch patches are preserved. Final native CI must verify the latest coordinated revisions.

Storage, import, and completion contracts

  • src/simplnx/Common/BoundedRead.hpp defines bounded reads with explicit scratch accounting and separate unavailable, failed, and successful outcomes. HDF5 reads validate selections before transfer.
  • src/simplnx/Filter/Actions/ImportH5ObjectPathsAction.cpp plans storage and geometry references for each selected destination and alias before publication.
  • src/simplnx/DataStructure/DataStructure.cpp owns import publication and rollback. It validates object identities and parent placements before mutation, preserves existing objects, and retains warnings and notifications.
  • src/simplnx/Pipeline/Pipeline.cpp propagates checked store-flush failures through pipeline completion. Recovery writes and nxrunner retain the corresponding errors and warnings.

Numeric copies and storage conversions retain growth initialization. Parameter clones retain storage-location restrictions. Large-data thresholds reject or correct invalid values with diagnostics. The preferences file stores them in GB with two decimals as large_data_size_gb and large_datastructure_size_gb. A legacy byte value loads on the 0.01-GB grid, and the next save writes the GB key. An invalid GB value uses the default with warning -592; an invalid legacy byte value keeps warning -591. Memory and the public API keep the values in bytes.

Filter correctness and bounded working memory

  • src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeFeatureCentroids.cpp computes periodic centroids from unwrapped cell centers. Bounded occupancy scans and a defined ambiguous-seam fallback support the documented numerical contract.
  • src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/IdentifySampleCCL.cpp batches YZ transfers. Mesh normals, angles, winding, shared faces, and smoothing also use bounded transfers with checked connectivity and partial grants.
  • src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/OrientationTupleLookup.hpp admits face buffers and orientation lookups together. A bounded page route handles metadata that does not fit a complete cache.
  • src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/LaplacianSmoothing.cpp supports temporary coordinate storage under constrained memory. Calculation failures preserve original coordinates; final-write failures can leave a completed prefix.

KMeans defaults to one cluster and rejects zero in both implementations. Smoothing, winding, and Zero Crossing honor shared algorithm-override precedence while retaining the storage checks required for direct access. Avizo writers check reads, writes, flushes, closes, and size arithmetic; their coordinate conventions and binary separators remain unchanged.

Regression coverage and maintainability

  • The tests use analytical centroid and mesh expectations, literal image values, complete output checks, and injected transfer failures. They cover cancellation, block tails, aliases, rollback, and override precedence.
  • test/UnitTestCommon/include/simplnx/UnitTest/ProcessTestProfile.hpp isolates process settings. Test scopes restore the incoming resolver and check cleanup.
  • Floating-array comparisons reject mismatched NaNs. Platform guards admit the reviewed standard-library configurations, and test-only interfaces remain conditional on their instrumentation.

Test plan

  • Coordinated Windows review builds passed. Affected selections passed 616/616 in-memory Release tests and 1,404/1,404 tests in each OOC Release and Debug configuration.
  • Both Windows Release aggregate builds passed after the local rebase. The rebase selections passed 191/191 in-memory and 199/199 OOC tests, with no skips.
  • Numerical checks include independent centroid, triangle, smoothing, and writer-output expectations. Disk-backed selections verify actual storage and outputs; an OOC algorithm flag alone is not treated as proof.
  • The complete three-PR C/C++ selection passed clang-format 19.1.1. Scoped static-analysis findings were triaged; no clean whole-project clang-tidy result is claimed.
  • The GB threshold change passed on macOS (Apple silicon) in the coordinated OOC configuration. The complete [ooc] selection passed 924/924 tests, and the new PreferencesSizeGBTest and the changed preference tests passed in OOC Release and OOC Debug.
  • Final Windows, Linux, and macOS CI passes on the published revisions, including the coordinated OOC configuration.

Laplacian smoothing still retains approximately 32 bytes per vertex in older resident working arrays. The whole filter is not yet bounded by its OOC allowance. The full all-filter coverage audit, general preference-type validation, array-kind policy, and library-version-gate redesign remain separate work.

Related Issue

Related to #1532 — expands algorithm and failure-path tests without completing the coverage inventory.

Related to #481 — expands geometry-reference, import, and persistence coverage without completing the HDF5 audit.

Related to #1768 — includes compatibility repairs; the allocation-policy redesign remains separate.

Related to #1835 — bounds newer smoothing buffers; the older resident arrays remain.

@jmarquisbq jmarquisbq self-assigned this Oct 5, 2026
@jmarquisbq jmarquisbq added bug Something isn't working Out-of-Core labels Oct 5, 2026
@jmarquisbq
jmarquisbq force-pushed the fix/ooc-review-findings branch from 476e193 to 9420aa3 Compare October 6, 2026 15:22
@imikejackson
imikejackson force-pushed the fix/ooc-review-findings branch 2 times, most recently from 027776a to 7f49132 Compare October 7, 2026 21:26
@imikejackson
imikejackson self-requested a review October 13, 2026 14:18
@imikejackson
imikejackson force-pushed the fix/ooc-review-findings branch from 1336ee8 to 9e87010 Compare October 16, 2026 19:54
@jmarquisbq
jmarquisbq force-pushed the fix/ooc-review-findings branch from 9e87010 to 3e376fe Compare October 19, 2026 15:48
@jmarquisbq
jmarquisbq force-pushed the fix/ooc-review-findings branch 2 times, most recently from 6c46cdd to 8483a45 Compare October 21, 2026 14:54
@jmarquisbq
jmarquisbq marked this pull request as ready for review October 23, 2026 13:43
@jmarquisbq
jmarquisbq force-pushed the fix/ooc-review-findings branch from 768d100 to 12d2887 Compare October 23, 2026 15:40
@joeykleingers

Copy link
Copy Markdown
Contributor

Commit b8a1a88 adds the fixes from a review of this pull request together with BlueQuartzSoftware/DREAM3DNX#1171 and BlueQuartzSoftware/SimplnxOoc#4.

  • Identify Sample (YZ mode, out-of-core storage): the first batch is published without extra planes. The first write to an imported read-only mask can need working memory from the same allowance as the extra planes; with an exact fill (for example four 64 MiB planes and a 1 GiB budget) that write failed. A regression test with a real imported mask failed before the fix.
  • Triangle normals (out-of-core output): the normal of triangle 0 is computed and written before the compute reservation is kept, and the plan is made again after that write. The step never reads the stored normals back. New error codes: -65817 (input read), -65818 (output write) and -65819 (the second reservation gets no memory). Cancellation after that write stops before the next reservation. When a later step fails, triangle 0 already has its new normal; the test TriangleNormalFilter: real HDF5 invalid connectivity now expects that.
  • DataStructure::flushChecked() stops at the first object that fails. It returns that object's errors with the warnings collected before it. The later objects are not flushed in that call and keep their changes in memory.
  • DiagnosticRepresentationSupported() accepts Apple libc++ 220106 (Xcode 27.0) with ABI 1, C++20 and ordinary new, after a review of the installed headers. The native support tests pass with it.
  • Compute Feature Centroids and Verify Triangle Winding use std::unique_ptr<std::vector<T>> buffers.

Verification on macOS arm64 Release: the affected selections pass in the in-core and out-of-core builds, and the complete out-of-core CTest suite passes except one load-sensitive visualization test in DREAM3DNX (see the comment on BlueQuartzSoftware/DREAM3DNX#1171). Windows, Linux and Debug builds were not run.

Keep parallel execution disabled after an earlier storage requirement or
explicit disable. Preserve the explicit enable call as the reset point.

Add regression cases for both helpers, mixed calls, empty/null inputs,
and the multicore enable/reset behavior.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Retain storage location when cloning an array parameter and accept
explicit matching in-memory and out-of-core stores. Keep metadata-only
and string-array behavior unchanged, with readable mismatch diagnostics.

Add the location and clone regression matrix.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Use one cluster by default and reject explicit zero during preflight and
both algorithm entry points before output changes or data access.

Update parameter help and contracts, and add literal one-cluster and
unchanged-output regressions for the direct and scanline paths.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
jmarquisbq and others added 24 commits October 28, 2026 12:04
Centralize the existing override order: ForceInCore first, ForceOOC
second, then physical storage. Use the same rule for both dispatch
overloads and triangle winding.

Add independent truth-table and cross-plugin state checks. Preserve
normal storage selection and data-access capability checks.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Use the shared override precedence for smoothing and record the selected
execution path.

Verify the resident and forced bounded paths with analytical K4
expectations and a small admitted working budget. Register the disk-
backed numerical contract in CTest.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Use the shared algorithm policy when resident buffers permit either
implementation. Keep the physical-store and concrete-buffer checks that
protect direct memory access.

Verify all force-flag combinations against literal output values,
execution counters, and transfer counts. Restore process state after
each case.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Make the raw-buffer owner noncopyable and nonmovable, with explicit
construction and compile-time ownership checks.

Clarify private names and add direct includes. Preserve buffer
accounting, read order, and cancellation behavior.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Add direct includes, const lock guards, fixed metadata arrays, and
consistent private names in the chunk helpers.

Retain runtime-sized buffers, codec arithmetic, the public callable
interface, and existing error behavior.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Use consistent private record names, direct includes, and explicit
result fields in import helpers.

Keep the publication record and all its callers in one change. Preserve
alias identities, ownership, rollback, notifications, and the public
import interfaces.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Add direct includes, const guards, explicit diagnostic fields, and clear
private names in pipeline and shared core helpers.

Preserve completion messages, error propagation, runtime storage, and
arithmetic order.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Add direct includes, const quaternion locals, and explicit index
grouping in the face-color and misorientation helpers.

Keep the admitted lookup buffers, numerical operations, serial store
access, and public interfaces unchanged.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Apply private-field names, direct includes, explicit index grouping, and
a constant scratch-size declaration in the centroid helper.

Preserve memory admission, data access, arithmetic order, and diagnostic
behavior.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Add the headers that directly declare the types used by KMeans.

Keep sampling, validation, and numerical behavior unchanged.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Use clear private names, direct includes, and explicit guards in the
bounded component-labeling helpers.

Keep traversal, storage admission, cancellation, and label results
unchanged.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Clarify private names, direct includes, ownership, and index expressions
in the mesh helpers.

Preserve coordinate arithmetic, storage order, cancellation, and
publication behavior. Correct the comment about completed flips after
cancellation.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Replace the two three-value per-triangle temporary vectors with fixed
arrays.

Retain the numerical operation order and input checks.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Use direct includes, clear local names, a const cleanup guard, and fixed
axis-name storage in the Avizo writers.

Keep output bytes, transfer sizes, failure propagation, and the public
writer interfaces unchanged.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Use required-object access in smoothing checks and typed floating-point
literals in the infinity comparison fixture.

Make the completion-failure fixture special members, includes, private
names, and diagnostic fields explicit. Preserve all failure injection
and expected results.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Delete copying and moving of the local geometry-reference index.
Default its destructor and preserve the existing borrowed-placement
lifetime and mapping behavior.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Use the scoped resolver guard during selected-import fixture setup.
Check nested ownership and the next storage decision after normal
return and exception unwinding.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
State the copy and move rules for scoped helpers and test fixtures.
Preserve the existing lifetime and ownership contracts.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Replace both built-in lookup arrays with std::to_array. Keep the same
characters, trailing null byte, and bounded diagnostic behavior.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Rename the scope guard capture to avoid the AppleClang shadow warning.
Keep the shared owner, move operation, and resolver restoration
unchanged.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Use the project pointer naming convention for six local variables
in the two selected-import policy tests. Preserve their lookups,
ownership checks, and expected results.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Replace private import access with owner-controlled publication.
Validate selected paths, identifiers, and parent/name placements
before mutation. Release action planning sets before publication.
Keep exact-object rollback, identifier reservation, notifications,
and allocation-free warning transfer together. Add literal ownership,
admission, rollback, warning, and checkpoint regression checks.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
Declare the missing special members for core and test helpers.
Preserve constructor availability, ownership, and exception behavior.
Keep source state when rvalue construction must retain copy behavior.

Signed-off-by: Jessica Marquis <jessica.marquis@bluequartz.net>
* Identify Sample: in YZ mode with out-of-core storage, publish the
  first batch without extra planes. The first write to an imported
  read-only mask can need working memory from the same allowance as
  the extra planes.
* Triangle normals: for out-of-core output, compute and write the
  normal of triangle 0 before the compute reservation is kept, then
  reserve and plan again. Failures of that step use the new codes
  -65817, -65818 and -65819. Cancellation after that write stops
  before the next reservation.
* DataStructure::flushChecked() stops at the first object that fails
  and returns its errors with the warnings collected before it. Later
  objects keep their unwritten changes.
* Accept Apple libc++ 220106 (Xcode 27.0) in
  DiagnosticRepresentationSupported() after a review of its headers.
* Use std::unique_ptr<std::vector<T>> buffers in Compute Feature
  Centroids and Verify Triangle Winding.

Signed-off-by: Joey Kleingers <joey.kleingers@bluequartz.net>
@jmarquisbq
jmarquisbq force-pushed the fix/ooc-review-findings branch from b8a1a88 to 98022e9 Compare October 28, 2026 18:24
* The preferences file now stores Large Data Size and Large Data
  Structure Size as large_data_size_gb and
  large_datastructure_size_gb, in GB (1024^3 bytes) with two decimals.
  The file does not store the byte keys.
* Memory keeps the thresholds in bytes. The public byte API does not
  change.
* loadFromFile reads a GB key first. When a GB key is absent, it reads
  the legacy byte key. It removes both keys and stores the value on
  the 0.01-GB grid. A value above 0 that rounds to 0 becomes 0.01 GB.
  The next save writes the GB key.
* An invalid GB value uses the default and gives warning -592. An
  invalid legacy byte value keeps warning -591.
* Add OocSizeHundredthsToBytes and OocSizeBytesToHundredths. File load,
  file save and the default use them, so a value that is saved and
  loaded again keeps its exact byte count.
* Add PreferencesSizeGBTest and update the tests that read the file.

Signed-off-by: Joey Kleingers <joey.kleingers@bluequartz.net>
* Catch2 converts the operands of a failed comparison to text. For a
  std::filesystem::file_time_type, libc++ formats a floating-point
  value, which needs macOS 13.3 or later. CI builds for macOS 11.7, so
  PreferencesSizeGBTest.cpp did not compile there.
* The comparison now gives a bool to CHECK, as in
  SelectiveImportReferencesTest.cpp.

Signed-off-by: Joey Kleingers <joey.kleingers@bluequartz.net>
joeykleingers added a commit to joeykleingers/simplnx that referenced this pull request Oct 28, 2026
* Entry 25 says to give CHECK and REQUIRE a bool when a test compares
  std::filesystem::file_time_type values or file-clock durations.
* Catch2 converts the operands to text, and libc++ then needs
  std::to_chars from macOS 13.3. The DREAM3D-NX macOS CI builds target
  macOS 11.7, so two tests in PR BlueQuartzSoftware#1764 did not compile there.

Signed-off-by: Joey Kleingers <joey.kleingers@bluequartz.net>

@imikejackson imikejackson left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jmarquisbq I request changes before this pull request merges. The bug fixes are good. My request is about the memory accounting in the bounded reads.

Request: count only data payload bytes against the memory budget

The bounded-read code tries to prove that each operation stays inside its memory grant, down to the byte. It counts Result objects, error vectors, message strings, empty std::vector headers, locks, spans and expected-lite temporaries. That proof depends on how each standard library allocates memory inside std::vector and std::string. For this reason, DiagnosticRepresentationSupported() (src/simplnx/Common/BoundedRead.hpp:110) is an allow-list of reviewed standard-library versions, and the bounded read refuses on any other compiler.

Please change the rule: the budget counts only buffers whose size changes with the data (compressed and decoded chunks, tiles, planes, working arrays). Fixed costs such as diagnostics, path copies, locks and spans are overhead, and the budget does not count them.

Reasons:

  1. The CacheMemoryBudgetManager header (src/simplnx/Utilities/CacheMemoryBudgetManager.hpp:32) says that the budget is not a total-application limit. Byte-exact proof of the error path gives more precision than the budget has.
  2. The allow-list breaks with each compiler update. This branch already has approximately six commits that only add a compiler to the list.
  3. DataStore::readExtentIntoBufferBounded() (src/simplnx/DataStructure/DataStore.hpp:472) allocates no scratch memory, but it refuses when the budget is less than 4096 bytes or when the compiler is not on the list.
  4. The accounting code, its comments and its tests are a large part of this pull request.

Changes in this repository

  • src/simplnx/Common/BoundedRead.hpp: keep Status, Code, Multiply(), Add() and ValidateExtent(). Remove DiagnosticRepresentationSupported(), EmptyVectorProxyBytes(), EmptyResultControlBytes(), EmptyInvalidResultControlBytes(), TerminalDiagnosticBoundBytes() with its static_assert (:521), ResultCapacityBytes(), ObserveTerminalForTesting() and k_DiagnosticBytes. Make FreshVectorBytes() a checked count * width, or remove it. Use the usual MakeErrorResult() path for diagnostics.
  • src/simplnx/DataStructure/DataStore.hpp:472: remove the first if block. The in-memory read ignores the budget and always reads.
  • src/simplnx/Utilities/Parsing/HDF5/BoundedChunkRead.cpp: remove the control-size sums (:295–:299), the path-capacity accounting (:492) and the allow-list checks (:499, :592). Do not subtract k_DiagnosticBytes (:597). Required bytes = compressed + decoded bytes of the active chunks. If the budget is too small, return Unavailable with an Insufficient warning that gives the required and available bytes.
  • Allocate data buffers with ScratchBuffer<T> or std::unique_ptr<std::byte[]>, so that the payload size is exact on every compiler.
  • Tests: remove test/DataStoreBulkTest.cpp:607 and the ResultCapacityBytes() assertions. Keep the value, validation, failure and cancellation tests. Add these budget tests: required − 1 gives Unavailable and an unchanged destination; the exact required bytes give Complete; an in-memory store with budget 0 reads correctly.

The companion pull requests BlueQuartzSoftware/SimplnxOoc#4 and BlueQuartzSoftware/DREAM3DNX#1171 need the same change in the out-of-core storage plugin and the histogram sampler. The complete design note for all three pull requests is in BlueQuartzSoftware/SimplnxOoc#4 (https://github.com/BlueQuartzSoftware/SimplnxOoc/pull/4#issuecomment-6504925290). Make the simplnx change first, because the two companions use this API.

#1768

After this change, there is no allow-list to replace. Please add Closes BlueQuartzSoftware/simplnx#1768 to the ## Related Issue section, and add a short comment to #1768 that tells why no replacement is necessary.

imikejackson pushed a commit to joeykleingers/simplnx that referenced this pull request Oct 30, 2026
* Entry 25 says to give CHECK and REQUIRE a bool when a test compares
  std::filesystem::file_time_type values or file-clock durations.
* Catch2 converts the operands to text, and libc++ then needs
  std::to_chars from macOS 13.3. The DREAM3D-NX macOS CI builds target
  macOS 11.7, so two tests in PR BlueQuartzSoftware#1764 did not compile there.

Signed-off-by: Joey Kleingers <joey.kleingers@bluequartz.net>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working Out-of-Core

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants