package unit; import java.util.*; /** * Panda3D CWE-407 benchmark: std::find on vector vs O(1) unordered set/map. * * Defect 1: panda/src/pgraph/camera.cxx line 252 * Camera::remove_display_region — std::find on small_vector * * Defect 2: panda/src/display/graphicsOutput.cxx line 1623 * GraphicsOutput::do_remove_display_region — std::find on pvector * Called during window teardown — all N regions removed sequentially → O(N²). * * Fix 1: Replace small_vector with pset. * insert/erase/contains all O(1). * Fix 2: Replace pvector with pmap. * find/erase O(1); iteration for active-region sort still works. * * tickets: docs/tickets/panda3d-0001-camera-display-regions-linear-find.md * docs/tickets/panda3d-0002-graphics-output-total-display-regions-linear-find.md */ public class Panda3DTest { // ----------------------------------------------------------------------- // SLOW: mirrors small_vector + std::find // ----------------------------------------------------------------------- static class SlowDisplayRegions { List regions = new ArrayList<>(); // Integer = simulated pointer id void add(int regionId) { regions.add(regionId); } /** Exact mirror of camera.cxx line 251-255 / graphicsOutput.cxx line 1622-1628. */ boolean remove(int regionId) { int idx = regions.indexOf(regionId); // std::find equivalent if (idx >= 0) { regions.remove(idx); return true; } return false; } } // ----------------------------------------------------------------------- // FAST: mirrors pset / pmap with O(1) ops // ----------------------------------------------------------------------- static class FastDisplayRegions { Set regions = new HashSet<>(); void add(int regionId) { regions.add(regionId); } boolean remove(int regionId) { return regions.remove(regionId); } } // ----------------------------------------------------------------------- // Bench harness // ----------------------------------------------------------------------- static void bench(String label, Runnable slow, Runnable fast, long sOps, long fOps) { slow.run(); fast.run(); long t0 = System.nanoTime(); slow.run(); long sMs = (System.nanoTime() - t0) / 1_000_000; long t1 = System.nanoTime(); fast.run(); long fMs = (System.nanoTime() - t1) / 1_000_000; double r = fOps > 0 ? (double) sOps / fOps : 0; System.out.printf(" %-52s slow:%4dms (%,d ops) fast:%4dms (%,d ops) speedup:%.0fx%n", label, sMs, sOps, fMs, fOps, r); } // ----------------------------------------------------------------------- // Scenarios // ----------------------------------------------------------------------- /** * Scenario 1 (panda3d-0001): Camera display region removal. * Add N regions, then remove them all (e.g., camera reassignment loop). */ static Runnable slowCameraRemoveAll(int n) { return () -> { SlowDisplayRegions dr = new SlowDisplayRegions(); for (int i = 0; i < n; i++) dr.add(i); for (int i = 0; i < n; i++) dr.remove(i); }; } static Runnable fastCameraRemoveAll(int n) { return () -> { FastDisplayRegions dr = new FastDisplayRegions(); for (int i = 0; i < n; i++) dr.add(i); for (int i = 0; i < n; i++) dr.remove(i); }; } /** * Scenario 2 (panda3d-0002): Window teardown — all display regions destroyed. * Deferred shading pipeline: N = render passes (shadow maps + g-buffer + post). * Each DisplayRegion destructor calls do_remove_display_region. */ static Runnable slowWindowTeardown(int n) { return () -> { SlowDisplayRegions total = new SlowDisplayRegions(); for (int i = 0; i < n; i++) total.add(i); // Teardown: regions destroyed in reverse-creation order (typical destructor order) for (int i = n - 1; i >= 0; i--) total.remove(i); }; } static Runnable fastWindowTeardown(int n) { return () -> { FastDisplayRegions total = new FastDisplayRegions(); for (int i = 0; i < n; i++) total.add(i); for (int i = n - 1; i >= 0; i--) total.remove(i); }; } /** * Scenario 3: VR multi-eye with repeated camera reassignment. * Each eye reassignment removes from old camera and adds to new — repeated K times * across N display regions. */ static Runnable slowVRReassign(int n, int k) { return () -> { SlowDisplayRegions cam = new SlowDisplayRegions(); for (int i = 0; i < n; i++) cam.add(i); for (int iter = 0; iter < k; iter++) { for (int i = 0; i < n; i++) { cam.remove(i); cam.add(i); } } }; } static Runnable fastVRReassign(int n, int k) { return () -> { FastDisplayRegions cam = new FastDisplayRegions(); for (int i = 0; i < n; i++) cam.add(i); for (int iter = 0; iter < k; iter++) { for (int i = 0; i < n; i++) { cam.remove(i); cam.add(i); } } }; } public static void main(String[] args) { System.out.println("Panda3D CWE-407: std::find on vector vs O(1) hash set/map"); System.out.println(" defect 1: panda/src/pgraph/camera.cxx line 252"); System.out.println(" defect 2: panda/src/display/graphicsOutput.cxx line 1623"); System.out.println(); int N = 800; long slowOps = (long) N * N / 2; bench(String.format("camera remove-all N=%d (panda3d-0001)", N), slowCameraRemoveAll(N), fastCameraRemoveAll(N), slowOps, N); bench(String.format("window teardown N=%d (panda3d-0002)", N), slowWindowTeardown(N), fastWindowTeardown(N), slowOps, N); bench(String.format("VR reassign N=%d x K=10 (panda3d-0001)", N), slowVRReassign(N, 10), fastVRReassign(N, 10), slowOps * 10, (long) N * 10); System.out.println(); System.out.println("Fix 1 (camera.cxx): pset — insert/erase O(1)."); System.out.println("Fix 2 (graphicsOutput.cxx): pmap — find/erase O(1)."); System.out.println("See patches panda3d-0001-* and panda3d-0002-*"); } }