Skip to content

Usd sender perf fix - #3

Merged
staticx-g7 merged 72 commits into
mainfrom
usd-sender-perf-fix
Sep 8, 2026
Merged

staticx-g7 merged 72 commits into
mainfrom
usd-sender-perf-fix

Conversation

@staticx-g7

Copy link
Copy Markdown
Collaborator

This is the full support changes:

  1. point cloud and meshes with materials.
  2. Full datatype support with zmq
  3. Actor configuration
  4. Benchmarking
  5. Zero copy

george2 and others added 30 commits July 28, 2025 12:56
…uad and trianlge accomodation, multiple uv set support, conditional triangulation, bug fix for wrong collision detection
…ertice count, forced rmc to use 32 bit, added initial benchmarking
staticx-g7 added 29 commits June 9, 2026 14:28
…e update rank threading

- Point cloud actors now named ElementName_r{rank} so all 16 ranks persist as separate actors
- Thread TargetRank through download chain: PipelineDownloadNext -> OnSingleFileDownloaded -> SpawnMeshFromData -> EnqueuePointCloud
- RefreshSingleFile removes old PC actors by _r{rank} suffix, re-queues with correct rank
- SetActorLabel on spawned PCs for unique outliner entries
- EJUSYNCExtension::USD now only accepts .usda (3 filter locations in BlueprintLibrary)
- Fix point cloud bounds transform to match position (x, z, -y)
- Fix C API mismatch for FileListWithSizesAndRanks (hash_lo/hash_hi params)
- DestroyAllActors in ClearSpawnedActors to prevent orphan accumulation
…n\nWas destroying wrong actors when _r{rank} matched multiple point clouds\n(e.g. spheres_0_r1 and spheres_1_r1 both matched _r1 suffix).\n\nNow extracts element name from clip filename and matches exact key.
- bInitialSpawnDone guard: skips FileUpdate notifications until
  OnAllComplete fires, preventing actor accumulation (18->25->33)
- RefreshedFiles TSet: deduplicates refreshes for same file per cycle
- Added PC key logging to RefreshSingleFile for debugging
- Clear RefreshedFiles on commit complete and start spawning
RetryFailedDownloads was spawning unbounded concurrent AsyncTasks,
and DiffAndRefreshFileList was spawning one per changed file, both
ignoring PipelineDepth. This caused SIGSEGV from heap corruption
under concurrent large-alloc pressure (35MB USD files).

Now all download paths share a single PipelineActive semaphore.
When PipelineActive >= PipelineDepth, remaining retries and
refreshes queue and chain from OnSingleFileDownloaded completion.

No mutexes added, no blocking on game thread, zero functionality
changes to existing pipeline flow.
DestroyAllActors and ClearSpawnedActors called Actor->Destroy()
with only a non-null pointer check. During PIE teardown, actors
were freed by one path and still referenced in another, causing
SIGSEGV in FName::AppendString when GetFullName was called on
a dangling pointer.
Middleware now deduplicates files with same stem but different USD
formats after receiving each file list. Priority: .usda > .usdc
> .usdz > .usd. Applied to all 4 C-API list entry points (sync +
async). Saves half the bandwidth and avoids spawning duplicate
actors.
PNG was tiny (~500 bytes) so LUT loaded instantly, but no PC
actors had spawned yet. RecolorGradientPendingActors() found 0
pending actors and returned. PCs later spawned as white with no
one to recolor them.

Now DrainReadyQueue checks GradientPendingActors after each batch
and recolors if LUT is already available. Handles both orderings:
1. LUT loads first, PCs later → recolor on drain exit
2. PCs spawn first, LUT later → recolor on SetGradientLUT call
Problem: OnSingleFileDownloaded called RetryRemainingFiles inline,
consuming FailedFileIndices before CheckAllDownloadsComplete could see
them. Also, parse failures were never retried.

Fix:
- Only CheckAllDownloadsComplete initiates retries (single point)
- Parse failures merge into FailedFileIndices for re-download retry
- PipelineActive > 0 guard prevents double-trigger while downloads in-flight
- All work stays async, game thread never blocked
…tire chain

Middleware changes:
- AnariUsdMessages.h: add NOTIFY_FILE_UPDATE_V2 (302) to enum, extend
  ZmqFileNotification with hashPrev128[2] + hasOldData, update
  isNotificationType + getMessageTypeName
- AnariUsdClient.cpp: handleNotification now logs V2 fields, passes
  hashLo/Hi/PrevLo/PrevHi/hasOldData to callback
- AnariUsdMiddleware_C.cpp: C callback signature extended with 5 hash params

UE plugin changes:
- JUSYNCTypes.h: FJUSYNCNotification struct extended with HashLo, HashHi,
  HashPrevLo, HashPrevHi, bHasOldData
- JUSYNCSubsystem.cpp: NotificationCallback_Static extended to parse all 5
  new params and broadcast on game thread

Both main and ThirdParty AnariUsdMessages.h copies updated (parity required).

Backward compatible: V1 notifications (type 300) still work — hashPrev fields
zeroed, hasOldData=false.
- Sync all ThirdParty header h files (V2 notification, hash fields)
- Sync BuildOutput ThirdParty headers
- Fix JUSYNCTypes.h uint64 -> int64 (UHT BlueprintType compat)
- Copy fresh libanari_usd_middleware.so to UE sample ThirdParty lib

Both builds verified:
- JUSYNC middleware: libanari_usd_middleware.so (131MB, GLIBC 2.2.5)
- UE sample: libUnrealEditor-JUSYNC.so (14.2s build, lld link)
LoadUSDBufferFromRaw now hands the caller's raw buffer directly to TinyUSDZ
when no ASCII rewrite is required (binary .usdc, clean .usda), eliminating
all copies for the streaming hot path. A single working copy is made only
when a length-changing USD quirk (0: None, asset:images/, texCoord2f) is
actually present.

- Add pointer-based LoadUSDBufferFromRaw overload delegating the vector one
- Byte-scan for quirks without materializing a std::string
- Reduce preprocessed-path copies from two to one
- Reflect accurate zero-copy semantics in C API docs/logs and UE labels
Empirically verified TinyUSDZ parses empty timeSampled arrays ('0: None')
and real point arrays ('0: [(x,y,z)...]') natively. Removing the '0: None'
-> '0: []' substitution from the scan so .usda files containing it now
take the true zero-copy path (caller buffer passed straight to parser).

Only the genuinely length-changing 'asset:images/' and 'texCoord2f'
substitutions remain as preprocessing triggers.
… downloads

- Dispatcher thread becomes the sole ZMQ recv owner (removes request/response race)
- Per-request_id response index: O(1) lookup, no silent drops
- Bounded 8-worker parallel-download pool with safe teardown
- requestFrame / requestWorkerListString routed through the per-id queue (quiescence preserved)
- De-linearized USD preprocessing (string_view find, static regex, no full-buffer copy)
- Demote hot-path INFO to DEBUG; DISABLE_DEBUG_LOGGING for non-Debug builds
- Remove unused MemoryMonitor / ParallelDownloadManager / ParallelDownloader + stray .backup
…UE plugin)

- Dedicated ParseMutex so the background USD parse never holds the game-thread MiddlewareMutex
- FileSpawner: file buffer now MOVED (TUniquePtr) across download->parse thread hops instead of memcpy'd
- BlueprintLibrary: drain ReceivedFiles via MoveTemp (removes deep copy + unbounded growth)
- Sync staged ThirdParty AnariUsdMiddleware_C.h with the canonical C API (add missing LoadUSDFullFromPointer_C decl)
GCC 14's new -Wdangling-pointer/-Wdangling-reference diagnostics in the vendored tinyusdz (tydra/scene-access.cc) become hard errors via tinyusdz's per-file -Werror and break a clean build. Force TINYUSDZ_NO_WERROR=ON for GNU >= 14 (mirrors the existing GCC>=15 -fpermissive workaround).
…joins in cleanup()

waitForMatchingFrames held responseQueueMutex (unique_lock) across the call
to tryDequeueMatching, which re-locks the same non-recursive mutex. glibc
parks the thread in the futex forever (self-lock on PTHREAD_MUTEX_NORMAL is
UB), so the first response dequeued from the wait path froze the request
thread; editor Stop then deadlocked in cleanup() on the same mutex.

- add tryDequeueMatchingLocked (caller holds the lock); slow path uses it
- cleanup(): set stop flags and notify both CVs BEFORE joining workers and
  dispatcher, so Stop can't stall behind in-flight request timeouts
…Win binaries under Lib/Win64

- CMake: CMP0104 NEW, add C language (external/xxhash/xxhash.c), set
  CMAKE_CUDA_ARCHITECTURES before add_library (CUDA 13 dropped sm_60/61/70)
- sync UnrealPlugin ThirdParty headers (ZmqConnector DEALER-only stub,
  UsdProcessor) with the rebuilt .so
- Windows binaries relocated to Lib/Win64/ (binaries untracked)
@staticx-g7
staticx-g7 merged commit 2f861eb into main Sep 8, 2026
9 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant