react-native-cesium 1.2.3 → 1.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -27
- package/android/CMakeLists.txt +1 -2
- package/android/src/main/cpp/CesiumBridgeAndroid.cpp +264 -130
- package/android/src/main/cpp/CesiumBridgeAndroid.h +70 -27
- package/android/src/main/java/com/margelo/nitro/reactnativecesium/CesiumBridgeJNI.kt +68 -44
- package/android/src/main/java/com/margelo/nitro/reactnativecesium/HybridCesiumView.kt +199 -187
- package/cpp/engine/CameraIntegrator.cpp +374 -0
- package/cpp/engine/CameraIntegrator.hpp +146 -0
- package/cpp/engine/EngineTunables.hpp +90 -31
- package/cpp/engine/GlobeCamera.cpp +17 -6
- package/cpp/engine/GlobeCamera.hpp +11 -8
- package/cpp/engine/test/CameraIntegratorTest.cpp +205 -0
- package/cpp/engine/test/build_and_run.sh +27 -0
- package/example/README.md +1 -1
- package/ios/CesiumBridge.h +53 -28
- package/ios/CesiumBridge.mm +172 -86
- package/ios/HybridCesiumView.swift +256 -145
- package/lib/typescript/src/specs/cesium-view.nitro.d.ts +85 -13
- package/lib/typescript/src/specs/cesium-view.nitro.d.ts.map +1 -1
- package/nitrogen/generated/android/ReactNativeCesiumOnLoad.cpp +2 -0
- package/nitrogen/generated/android/c++/JFunc_void_CameraState.hpp +77 -0
- package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.cpp +108 -9
- package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.hpp +21 -3
- package/nitrogen/generated/android/c++/views/JHybridCesiumViewStateUpdater.cpp +24 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/Func_void_CameraState.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/HybridCesiumViewSpec.kt +71 -3
- package/nitrogen/generated/ios/ReactNativeCesium-Swift-Cxx-Bridge.cpp +3 -3
- package/nitrogen/generated/ios/ReactNativeCesium-Swift-Cxx-Bridge.hpp +43 -28
- package/nitrogen/generated/ios/c++/HybridCesiumViewSpecSwift.hpp +89 -9
- package/nitrogen/generated/ios/c++/views/HybridCesiumViewComponent.mm +30 -0
- package/nitrogen/generated/ios/swift/Func_void_CameraState.swift +5 -5
- package/nitrogen/generated/ios/swift/HybridCesiumViewSpec.swift +15 -3
- package/nitrogen/generated/ios/swift/HybridCesiumViewSpec_cxx.swift +242 -16
- package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.cpp +21 -3
- package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.hpp +22 -4
- package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.cpp +66 -0
- package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.hpp +6 -0
- package/nitrogen/generated/shared/json/CesiumViewConfig.json +6 -0
- package/package.json +1 -1
- package/src/specs/cesium-view.nitro.ts +102 -14
- package/cpp/engine/CameraSmoother.cpp +0 -68
- package/cpp/engine/CameraSmoother.hpp +0 -28
- package/cpp/engine/CameraTargetState.cpp +0 -205
- package/cpp/engine/CameraTargetState.hpp +0 -126
package/README.md
CHANGED
|
@@ -280,7 +280,7 @@ When you pass a callback ref to `CesiumView`, wrap it with `callback(...)` from
|
|
|
280
280
|
| --- | --- | --- |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
281
281
|
| `ionAccessToken` | `string` | _none_ | Cesium Ion token for authenticated asset/imagery requests. An invalid token usually leaves the globe empty or partially loaded due to 401/403 responses. |
|
|
282
282
|
| `ionAssetId` | `number` | _none_ | Main Ion asset to render (tileset/terrain). |
|
|
283
|
-
| `initialCamera` | `CameraState` | _none_ |
|
|
283
|
+
| `initialCamera` | `CameraState` | _none_ | Camera applied via `teleport(...)` once when the view is created. **Only the initial mount writes this value**; subsequent prop changes are ignored. Use the per-DoF setters (`setPosition` / `setAltitude` / `setHeading` / `setAttitude` / `setVerticalFov`) or `teleport(...)` for runtime moves. |
|
|
284
284
|
| `pauseRendering` | `boolean` | `false` | Pauses the native render loop. Set `true` to stop rendering and reduce GPU/CPU usage. |
|
|
285
285
|
| `maximumScreenSpaceError` | `number` | `32` | Quality/performance trade-off for tile refinement. Lower values are sharper (more work); higher values are faster/blurrier. |
|
|
286
286
|
| `maximumSimultaneousTileLoads` | `number` | `12` | Max concurrent tile fetch/decode operations. Raising `8 -> 16` can improve fast camera moves on good networks but may increase memory/bandwidth spikes. |
|
|
@@ -312,6 +312,11 @@ These props are all optional. Leave them unset to use the built-in defaults; set
|
|
|
312
312
|
| `lodTransitionLength` | `number` | `1.0` | Length in seconds of the LOD cross-fade when `enableLodTransitionPeriod` is `true`. |
|
|
313
313
|
| `sqliteCacheMaxRows` | `number` | `4096` | Maximum number of cached HTTP responses Cesium keeps in `cesium_cache.db` between sessions. Tablets can comfortably go to `16384`. **Re-applied only on the next view init** — runtime changes take effect after the next mount. |
|
|
314
314
|
| `taskProcessorThreads` | `number` | `0` (auto) | Worker pool size for tile parsing / texture decode. `0` (default) auto-sizes to `clamp(2, std::thread::hardware_concurrency() - 1, 8)`. Override for benchmarking or to throttle background CPU on shared-render apps. **Re-applied only on the next view init.** |
|
|
315
|
+
| `maxYawRateDegSec` | `number` | `0` (uncapped, but compile-time default `360`) | Hard ceiling on the integrator's heading velocity (deg/s). Useful when your heading feed is noisy and you would rather drop a sudden spike than visually spin the camera. |
|
|
316
|
+
| `maxPitchRateDegSec` | `number` | `0` (uncapped, but compile-time default `360`) | Hard ceiling on the integrator's pitch velocity (deg/s). |
|
|
317
|
+
| `maxRollRateDegSec` | `number` | `0` (uncapped, but compile-time default `720`) | Hard ceiling on the integrator's roll velocity (deg/s). |
|
|
318
|
+
| `maxClimbRateMps` | `number` | `0` (uncapped) | Hard ceiling on the integrator's altitude velocity (m/s). |
|
|
319
|
+
| `maxGroundSpeedMps` | `number` | `0` (uncapped) | Hard ceiling on the integrator's horizontal ground velocity (m/s). |
|
|
315
320
|
|
|
316
321
|
#### Suggested presets
|
|
317
322
|
|
|
@@ -361,7 +366,7 @@ Use this when matching **Skia**, **custom HUDs**, or **CesiumJS** math to the sa
|
|
|
361
366
|
|
|
362
367
|
### `Quaternion`
|
|
363
368
|
|
|
364
|
-
Used for **camera-space view correction** with `
|
|
369
|
+
Used for **camera-space view correction** with `setViewCorrection(q)` (see below). Components are `w, x, y, z` (Hamilton convention). Non-unit values are normalized on the native side.
|
|
365
370
|
|
|
366
371
|
```ts
|
|
367
372
|
type Quaternion = {
|
|
@@ -374,45 +379,87 @@ type Quaternion = {
|
|
|
374
379
|
|
|
375
380
|
### `CesiumViewMethods`
|
|
376
381
|
|
|
377
|
-
|
|
378
|
-
| --- | --- |-----------------------------------------------------------------------------------------------------------------------------------------------|
|
|
379
|
-
| `setCamera` | `(camera: CameraState) => void` | Applies a new camera state at runtime (position, heading, pitch, roll, vertical FOV). Does **not** change the stored view-correction quaternion; use `setCameraQuaternion` when you need to update that. |
|
|
380
|
-
| `setCameraQuaternion` | `(camera: CameraState, viewCorrection: Quaternion) => void` | Same camera fields as `setCamera`, plus a rotation applied **in camera space after** heading/pitch/roll. Use this for screen-fixed adjustments (e.g. boresight calibration, aligning a synthetic horizon overlay) without expressing the offset as extra Euler angles. |
|
|
381
|
-
| `getCameraState` | `() => Promise<CameraState>` | Returns the current native camera snapshot (lat/lon/alt, HPR, VFOV). This is the underlying globe attitude; it does **not** encode the view correction into HPR. |
|
|
382
|
-
| `getViewCorrection` | `() => Promise<Quaternion>` | Returns the **smoothed** view-correction quaternion currently applied (identity `w=1,x=y=z=0` if you have never called `setCameraQuaternion`). |
|
|
382
|
+
Each axis (position, altitude, heading, attitude, viewCorrection, verticalFov) has its own α-β tracker on the native side, so you only need to push the values that actually changed. Combined with a `teleport(...)` for hard jumps and a `getActualCamera()` / `getDemandCamera()` pair for read-back, this is the entire camera API.
|
|
383
383
|
|
|
384
|
-
|
|
384
|
+
| Method | Signature | Description |
|
|
385
|
+
| --- | --- | --- |
|
|
386
|
+
| `setPosition` | `(latitude: number, longitude: number) => void` | New geographic position **demand** (degrees). Each call stamps the measurement with the native steady-clock time of arrival and runs one α-β update against the predicted state at that instant. Latitude is clamped to ±90 on extrapolation; longitude is continuous (no wrap normalisation) so shortest-arc residual maths stays correct across the antimeridian. |
|
|
387
|
+
| `setAltitude` | `(altitudeMeters: number) => void` | New altitude demand (metres MSL). |
|
|
388
|
+
| `setHeading` | `(headingDeg: number) => void` | New heading demand (degrees; `0` = north, increasing clockwise). The integrator computes the shortest-arc residual, so feeding it `359 → 1` rotates 2° clockwise, not 358° counter-clockwise. |
|
|
389
|
+
| `setAttitude` | `(pitchDeg: number, rollDeg: number) => void` | New pitch and roll demand (degrees). Bundled because almost every IMU emits them together — but you can pass the previous value for either axis if only one of them has changed. |
|
|
390
|
+
| `setViewCorrection` | `(q: Quaternion) => void` | New camera-space rotation applied **after** HPR. SLERPed toward the latest demand each frame. Use for boresight calibration, screen-fixed HUD alignment, etc. Non-unit values are normalised on the native side. |
|
|
391
|
+
| `setVerticalFov` | `(deg: number) => void` | New vertical FOV demand (degrees). Clamped to `20..100` on the native side. |
|
|
392
|
+
| `teleport` | `(camera: CameraState) => void` | Hard scene jump — atomically resets every DoF (value, velocity, demand) to `camera`. Bypasses the integrator entirely. Use for `Fly to coordinate` actions or seeding from saved state. |
|
|
393
|
+
| `getActualCamera` | `() => Promise<CameraState>` | Most recently rendered camera (post-integration). This is what the user is currently looking at and what a HUD overlay should mirror. Differs from `getDemandCamera()` while a glide is in progress or when the terrain-floor clamp has raised the altitude. |
|
|
394
|
+
| `getDemandCamera` | `() => Promise<CameraState>` | What the consumer last asked for (per-DoF demand). Useful for diagnostics, or for showing the requested camera alongside the rendered one. |
|
|
395
|
+
| `getViewCorrection` | `() => Promise<Quaternion>` | Current view-correction quaternion (smoothed toward the latest demand). Identity (`w=1, x=y=z=0`) if you have never called `setViewCorrection`. |
|
|
396
|
+
|
|
397
|
+
#### Threading: setters vs getters
|
|
385
398
|
|
|
386
399
|
| Call style | Supported / best practice |
|
|
387
400
|
| --- | --- |
|
|
388
|
-
| **`
|
|
389
|
-
| **`
|
|
401
|
+
| **`setPosition` / `setAltitude` / `setHeading` / `setAttitude` / `setViewCorrection` / `setVerticalFov` / `teleport`** | **Synchronous** native updates. **Supported** from the **UI thread**, including **Reanimated worklets** and **`useAnimatedReaction`** when you call through a Nitro `hybridRef`. The integrator takes its mutex briefly (uncontested in practice) and returns immediately — this is the intended path for high-frequency camera updates (e.g. a 50 Hz IMU feed). |
|
|
402
|
+
| **`getActualCamera` / `getDemandCamera` / `getViewCorrection`** | **Async** (`Promise`). **Call from the JavaScript thread** (e.g. `useEffect`, handlers, throttled HUD state) — not from worklets — unless you have a clear, tested pattern for async in your runtime. |
|
|
390
403
|
|
|
391
404
|
Avoid assuming **main-thread-only** vs **JS-thread-only** labels beyond the above: Nitro invokes the hybrid object on the thread that called the method; use sync setters on the UI/worklet path you already use for gestures, and reserve Promise-based getters for JS.
|
|
392
405
|
|
|
393
|
-
####
|
|
406
|
+
#### Choosing the right setter
|
|
407
|
+
|
|
408
|
+
- Stream **only what changed**. A 1 Hz GPS fix that does not move you should not call `setPosition` every second; a 50 Hz IMU does not need to call `setPosition` at all.
|
|
409
|
+
- Use **`teleport`** when the new camera has no continuous relationship to the previous one — "Fly to coordinate", loading a saved view, an in-app scene switch. Teleport zeroes velocity, so the camera will start gliding from rest the next time a setter is called.
|
|
410
|
+
- Use **`setViewCorrection(identity)`** to clear a calibration quaternion. `teleport` deliberately preserves the latest view-correction target — it is a position/orientation jump, not a boresight reset.
|
|
411
|
+
|
|
412
|
+
#### Demand vs actual camera
|
|
413
|
+
|
|
414
|
+
The integrator tracks two camera states simultaneously:
|
|
415
|
+
|
|
416
|
+
- **Demand** — what the consumer asked for, exactly as it was passed to the setters. `getDemandCamera()` returns this.
|
|
417
|
+
- **Actual** — what the integrator extrapolated to and what was passed to the GPU on the last frame. `getActualCamera()` returns this.
|
|
418
|
+
|
|
419
|
+
For a steady GPS feed with no terrain clamp the two values converge within a few frames. They diverge when:
|
|
394
420
|
|
|
395
|
-
-
|
|
396
|
-
-
|
|
397
|
-
-
|
|
398
|
-
|
|
421
|
+
- A glide is in progress (you just called `setPosition` and the α-β tracker is still chasing the demand).
|
|
422
|
+
- The terrain-floor clamp (`minAltitudeAboveTerrain`) has raised the actual altitude above the demand.
|
|
423
|
+
- A sudden change is in flight and you want to display the requested value rather than the partially-tracked one (e.g. a HUD).
|
|
424
|
+
|
|
425
|
+
`hudCamera` in the example app reads from `getActualCamera()` via a worklet-side mirror so what the user sees in the HUD matches what is on screen.
|
|
399
426
|
|
|
400
427
|
#### Smoothing of incoming camera updates
|
|
401
428
|
|
|
402
|
-
Every value you pass to
|
|
429
|
+
Every value you pass to a setter is treated as a **demand** target — what you would *like* the camera to be — rather than a per-frame command. The native render thread runs an α-β (constant-velocity) tracker per DoF and extrapolates to the next vsync, which means a coarse 1 Hz position feed (e.g. external GPS) does **not** teleport the view once a second. Instead it moves at the implied ground speed between fixes — exactly like looking out the window of the vehicle being tracked.
|
|
403
430
|
|
|
404
431
|
| DoF | Smoothing |
|
|
405
432
|
| --- | --- |
|
|
406
|
-
| `latitude` / `longitude` |
|
|
407
|
-
| `altitude` |
|
|
408
|
-
| `heading` |
|
|
409
|
-
| `pitch` / `roll` |
|
|
410
|
-
| `viewCorrection` (quaternion) |
|
|
411
|
-
| `verticalFovDeg` |
|
|
433
|
+
| `latitude` / `longitude` | α-β tracker. Velocity is learned from inter-arrival times. Steady at constant motion (zero lag at constant ground speed), gliding through brief network gaps. Optional outlier rejection (`kOutlierLatLonDeg`) drops a single measurement whose residual exceeds the threshold. |
|
|
434
|
+
| `altitude` | α-β tracker. Self-adapts whether altitude comes from GPS (1 Hz, learned m/s climb) or a barometer (50 Hz, ≈zero glide). |
|
|
435
|
+
| `heading` | α-β tracker, residual on shortest-arc angular delta. Antimeridian-safe. Yaw rate is the learned velocity, capped by `maxYawRateDegSec`. |
|
|
436
|
+
| `pitch` / `roll` | α-β tracker, residual on shortest-arc angular delta. Constant-rate roll/pitch under steady IMU input; capped by `maxPitchRateDegSec` / `maxRollRateDegSec`. |
|
|
437
|
+
| `viewCorrection` (quaternion) | Single-coefficient SLERP toward the latest demand each frame, hemisphere-corrected. |
|
|
438
|
+
| `verticalFovDeg` | α-only (no velocity term). |
|
|
439
|
+
|
|
440
|
+
You don't have to do anything special to take advantage of this — the integrator runs whenever a frame is rendered, regardless of whether your driver is a Reanimated worklet at 60 Hz, a `setInterval` push at 10 Hz, or a 1 Hz GPS subscription. Just call the appropriate per-DoF setter when you have a new value, and call `teleport(...)` when you want to bypass smoothing entirely.
|
|
441
|
+
|
|
442
|
+
Each DoF also tracks the EWMA of its measurement inter-arrival time and bleeds the learned velocity exponentially once measurements stop arriving (silence > `1.5 ×` mean interval). This keeps a 60 Hz worklet feed from "coasting" the camera after the user releases a pan gesture or returns the joystick to centre, while leaving a 1 Hz GPS glide untouched between fixes — bleed never engages unless the sensor falls measurably behind its usual cadence.
|
|
412
443
|
|
|
413
|
-
|
|
444
|
+
If the smoothing ever feels off for your data source, the relevant constants live in [`cpp/engine/EngineTunables.hpp`](cpp/engine/EngineTunables.hpp): `kAlphaPos` / `kBetaPos`, `kAlphaAlt` / `kBetaAlt`, `kAlphaHdg` / `kBetaHdg`, `kAlphaPitch` / `kBetaPitch`, `kAlphaRoll` / `kBetaRoll`, `kAlphaVfov`, `kAlphaViewCorr`. The velocity bleed is tuned via `kVelocitySilenceGraceFactor`, `kVelocityBleedTauFactor`, and `kIntervalEwmaAlpha`. Per-DoF rate caps and optional outlier thresholds are also defined there (`kMaxYawRateDegSec`, `kMaxPitchRateDegSec`, `kMaxRollRateDegSec`, `kMaxClimbRateMps`, `kMaxGroundSpeedMps`, `kMaxFrameDtSec`, `kOutlierLatLonDeg`, `kOutlierAltMeters`, `kOutlierHeadingDeg`). Use the `maxYawRateDegSec` etc. view props to override the caps at runtime.
|
|
414
445
|
|
|
415
|
-
|
|
446
|
+
#### Driving the camera from native sensors
|
|
447
|
+
|
|
448
|
+
High-rate sensors can feed their matching setter directly — there is no need to round-trip through a shared `CameraState` value:
|
|
449
|
+
|
|
450
|
+
```ts
|
|
451
|
+
gpsSubscription.on('fix', (fix) => {
|
|
452
|
+
cesiumViewRef.current?.setPosition(fix.latitude, fix.longitude)
|
|
453
|
+
cesiumViewRef.current?.setAltitude(fix.altitudeMsl)
|
|
454
|
+
})
|
|
455
|
+
|
|
456
|
+
imuSubscription.on('attitude', (att) => {
|
|
457
|
+
cesiumViewRef.current?.setHeading(att.yawDeg)
|
|
458
|
+
cesiumViewRef.current?.setAttitude(att.pitchDeg, att.rollDeg)
|
|
459
|
+
})
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
Each setter is synchronous, takes the integrator's mutex briefly, and is safe to call at any rate from any thread.
|
|
416
463
|
|
|
417
464
|
### `CesiumMetrics`
|
|
418
465
|
|
|
@@ -432,8 +479,9 @@ If the smoothing ever feels off for your data source, the relevant constants liv
|
|
|
432
479
|
The example app in [`example/App.tsx`](./example/App.tsx) is the best current integration reference. It shows:
|
|
433
480
|
|
|
434
481
|
- creating and storing a Nitro `hybridRef`
|
|
435
|
-
- driving camera updates
|
|
436
|
-
-
|
|
482
|
+
- driving camera updates via per-DoF setters from a Reanimated **`useAnimatedReaction`** (see [`example/hooks/useCameraController.ts`](./example/hooks/useCameraController.ts)) — note how the reaction diffs each DoF and only calls the setters that actually changed
|
|
483
|
+
- a one-shot `getActualCamera()` after mount so the gesture-handler pan anchors track the terrain-clamped altitude (see same hook)
|
|
484
|
+
- the `setViewCorrection` / `getViewCorrection` APIs for camera-space HUD alignment (the example does not demonstrate them yet; see **`CesiumViewMethods`** above)
|
|
437
485
|
- listening to `onMetrics`
|
|
438
486
|
- switching imagery layers
|
|
439
487
|
- presenting Cesium attribution from `creditsPlainText`
|
|
@@ -442,6 +490,31 @@ Before running the example, copy `example/.env_example` to `example/.env` and se
|
|
|
442
490
|
|
|
443
491
|
The example project currently links this library locally via `link:..`.
|
|
444
492
|
|
|
493
|
+
## Troubleshooting
|
|
494
|
+
|
|
495
|
+
### Noisy logs during fast refresh
|
|
496
|
+
|
|
497
|
+
When React Native fast-refresh (or `r` from the Metro terminal) tears down
|
|
498
|
+
the current bundle and re-mounts the view, you may briefly see one or both
|
|
499
|
+
of the following in the device log:
|
|
500
|
+
|
|
501
|
+
- `[AsyncJSCallback] Failed to call AsyncJSCallback<...> - the Dispatcher has already been destroyed!`
|
|
502
|
+
- `[SqliteCache.cpp:...] database is locked`
|
|
503
|
+
|
|
504
|
+
Both are harmless. The first means a queued `onMetrics` callback reached the
|
|
505
|
+
JS side after the old runtime's dispatcher had already been torn down; the
|
|
506
|
+
view's render-thread teardown drops the callback before invoking it on the
|
|
507
|
+
next refresh. The second comes from Cesium Native's `SqliteCache` and only
|
|
508
|
+
appears when a new `CesiumView` opens the on-disk tile cache while the old
|
|
509
|
+
one is still in the process of releasing it; the cache writer simply skips
|
|
510
|
+
the contended row and retries on the next request. Neither indicates a
|
|
511
|
+
bug in your code, and neither appears in release builds without
|
|
512
|
+
fast-refresh churn.
|
|
513
|
+
|
|
514
|
+
If you do see these in production (i.e., not during fast refresh), make
|
|
515
|
+
sure you are not mounting two `<CesiumView />` instances against the same
|
|
516
|
+
cache directory simultaneously.
|
|
517
|
+
|
|
445
518
|
## Credits
|
|
446
519
|
|
|
447
520
|
- Native rendering is built on [Cesium Native](https://github.com/CesiumGS/cesium-native)
|
package/android/CMakeLists.txt
CHANGED
|
@@ -71,8 +71,7 @@ add_library(${PACKAGE_NAME} SHARED
|
|
|
71
71
|
../cpp/engine/GlobeCamera.cpp
|
|
72
72
|
../cpp/engine/GeoidConverter.cpp
|
|
73
73
|
../cpp/engine/TileLifecycleManager.cpp
|
|
74
|
-
../cpp/engine/
|
|
75
|
-
../cpp/engine/CameraTargetState.cpp
|
|
74
|
+
../cpp/engine/CameraIntegrator.cpp
|
|
76
75
|
../cpp/engine/MetricsAggregator.cpp
|
|
77
76
|
../cpp/third_party/egm96/EGM96.c
|
|
78
77
|
src/main/cpp/vulkan/VulkanBackend.cpp
|