Skip to content

pixel index overshoots our allocated array canvas #252

Open

Description

@lruminski

I've been debugging several crashes and found a bug on gsplat_cpu.cpp:281

                        float T = pFinalTs[pixIdx]

should be

                        // Safety Check: If our parallel pixel index overshoots our allocated array canvas, safely skip it!
                        if (pixIdx >= (width * height)) {
                          continue;
                        }

                        float T = pFinalTs[pixIdx]

width == 1718
height == 2288

however pixIdx == 3961716 which is greater than 3930784

Activity

  1. lruminski commented on Oct 7, 2026

    @lruminski
    Author
    Image
  2. lruminski commented on Oct 7, 2026

    @lruminski
    Author

    could it be that i was using an undistorted image set?

  3. lruminski commented on Oct 7, 2026

    @lruminski
    Author

    no, still happens with the original image set.

  4. lruminski commented on Oct 7, 2026

    @lruminski
    Author

    do you think I need to check if the index is negative as well @pierotofy?

  5. lruminski commented on Oct 8, 2026

    @lruminski
    Author

    it's also crashing on gsplat_cpu.cpp:266,
    for some reason minx and maxx are both very large negative values.

    I'm going back to v1.5.5 to see if I'm getting similar crashes.

  6. lruminski commented on Oct 8, 2026

    @lruminski
    Author
    // --- Inside gsplat_cpu.cpp right after minx/maxx/miny/maxy are calculated ---
    
    // 1. Intercept NaN or Infinite coordinates caused by bad Gaussian splits
    if (std::isnan(minx) || std::isnan(maxx) || std::isinf(minx) || std::isinf(maxx) ||
        std::isnan(miny) || std::isnan(maxy) || std::isinf(miny) || std::isinf(maxy)) {
        continue; // Safely skip this broken Gaussian point and proceed to the next one
    }
    
    // 2. Clamp values to your legal screen boundaries to prevent massive underflows
    minx = std::max(0, std::min(minx, width - 1));
    maxx = std::max(0, std::min(maxx, width - 1));
    miny = std::max(0, std::min(miny, height - 1));
    maxy = std::max(0, std::min(maxy, height - 1));
    
    

    According to AI:

    Between version v1.1.5 and your current version v1.2.2, OpenSplat went through significant structural updates. The exact regression you are seeing is directly tied to a major tile-rasterization rewrite introduced in this release window.
    The most relevant commit to look at is "Fix tile-bin overflow, compressed image cache" (#250). Here is the breakdown of how that change introduced your specific crash when handling larger datasets on the CPU:

    1. The Tile-Bin Optimization Vector Overflow (The Root Issue)

    In versions prior to v1.1.5, OpenSplat’s CPU rasterizer mapped 3D Gaussians to screen space using a conservative fixed matrix layout. To optimize rendering performance for larger scenes, the project maintainer introduced a more aggressive dynamic tile-binning mechanism.
    • The Intended Change: It calculates a tight bounding box (minx, maxx, miny, maxy) around each projected splat, grouping them into 16×16 spatial tile indices to optimize thread dispatching.
    • The Unintended Bug: The math assumes a standard, stable field-of-view matrix. When a scene suddenly scales up (like your jump to 140,472 initial points and 201 images), the densification interval at step 500 pushes newly generated, unrefined points to the absolute periphery of the projection plane. The formulas do not feature proper saturation or edge checks, causing minx and maxx to experience integer underflow/overflow bounds checking failure (slipping into deep negative integer limits).

    1. Multi-Threaded Cache Mismatches

    Commit #250 also introduced a complex compressed image memory cache system designed to keep VRAM/RAM lean.
    • The Bug: When rendering on the CPU, different thread processes dynamically scale and compress/decompress active texture blocks on-the-fly based on your image downsample scaling parameter (-d 2).
    • The Collision: At step 500, a massive vector memory reallocation event fires off to accommodate the thousands of newly spawned points. The multi-threaded rasterizer threads continue calculating bounding footprints using the downscaled frame size tracker, but the underlying shared tile-state cache size matrices (pFinalTs) temporarily desynchronize during the rapid memory update. This results in the thread tracking an invalid index number (like 3961716), walking right over the allocated bounds of the array, and triggering the hard 0xC0000005 Access Violation.

    Why Your In-Line Filters Fixed It

    By inserting our std::isnan / std::isinf guards and clamping minx and maxx tightly against (width - 1), you directly intercepted the math that this v1.2.2 optimization pass missed. You forced the new tile-binning logic to safely drop the extreme projection vectors that were missing validation checks in the current master repository branch!

  7. lruminski commented on Oct 8, 2026

    @lruminski
    Author

    I'm going to let v1.1.5 run for a few thousand iterations and then compare the results. I haven't applied the In-Line filtering as AI is suggesting yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions