react-native-cesium 1.2.3 → 1.2.4
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 +160 -187
- package/cpp/engine/CameraIntegrator.cpp +374 -0
- package/cpp/engine/CameraIntegrator.hpp +146 -0
- package/cpp/engine/EngineTunables.hpp +78 -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 +213 -145
- package/lib/typescript/src/specs/cesium-view.nitro.d.ts +77 -13
- package/lib/typescript/src/specs/cesium-view.nitro.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.cpp +90 -9
- package/nitrogen/generated/android/c++/JHybridCesiumViewSpec.hpp +19 -3
- package/nitrogen/generated/android/c++/views/JHybridCesiumViewStateUpdater.cpp +20 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativecesium/HybridCesiumViewSpec.kt +57 -3
- package/nitrogen/generated/ios/ReactNativeCesium-Swift-Cxx-Bridge.hpp +9 -9
- package/nitrogen/generated/ios/c++/HybridCesiumViewSpecSwift.hpp +82 -9
- package/nitrogen/generated/ios/c++/views/HybridCesiumViewComponent.mm +25 -0
- package/nitrogen/generated/ios/swift/HybridCesiumViewSpec.swift +14 -3
- package/nitrogen/generated/ios/swift/HybridCesiumViewSpec_cxx.swift +210 -16
- package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.cpp +19 -3
- package/nitrogen/generated/shared/c++/HybridCesiumViewSpec.hpp +20 -4
- package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.cpp +55 -0
- package/nitrogen/generated/shared/c++/views/HybridCesiumViewComponent.hpp +5 -0
- package/nitrogen/generated/shared/json/CesiumViewConfig.json +5 -0
- package/package.json +1 -1
- package/src/specs/cesium-view.nitro.ts +93 -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/ios/CesiumBridge.mm
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
#import "CesiumBridge.h"
|
|
2
2
|
#import <CoreFoundation/CoreFoundation.h>
|
|
3
3
|
|
|
4
|
-
#include "engine/
|
|
5
|
-
#include "engine/CameraTargetState.hpp"
|
|
4
|
+
#include "engine/CameraIntegrator.hpp"
|
|
6
5
|
#include "engine/CesiumEngine.hpp"
|
|
7
6
|
#include "engine/EngineTunables.hpp"
|
|
8
7
|
#include "engine/GeoidConverter.hpp"
|
|
@@ -13,6 +12,7 @@
|
|
|
13
12
|
|
|
14
13
|
#include <algorithm>
|
|
15
14
|
#include <atomic>
|
|
15
|
+
#include <chrono>
|
|
16
16
|
#include <cmath>
|
|
17
17
|
#include <memory>
|
|
18
18
|
#include <string>
|
|
@@ -22,15 +22,25 @@
|
|
|
22
22
|
std::unique_ptr<reactnativecesium::CesiumEngine> _engine;
|
|
23
23
|
reactnativecesium::FrameResult _frameResult;
|
|
24
24
|
|
|
25
|
-
reactnativecesium::EngineConfig
|
|
26
|
-
reactnativecesium::
|
|
27
|
-
reactnativecesium::MetricsAggregator
|
|
25
|
+
reactnativecesium::EngineConfig _config;
|
|
26
|
+
std::unique_ptr<reactnativecesium::CameraIntegrator> _integrator;
|
|
27
|
+
reactnativecesium::MetricsAggregator _metrics;
|
|
28
|
+
|
|
29
|
+
// Force-render flag — set on every demand change so the render loop knows
|
|
30
|
+
// to dispatch at least one frame to advance the integrator. Cleared once
|
|
31
|
+
// the integrator reports isActive() == false.
|
|
32
|
+
std::atomic<bool> _forceRender;
|
|
28
33
|
|
|
29
34
|
int _viewportWidth;
|
|
30
35
|
int _viewportHeight;
|
|
31
36
|
BOOL _initialized;
|
|
32
37
|
BOOL _suspended;
|
|
33
38
|
|
|
39
|
+
// Last wall-clock tick used to compute the metrics-only frame dt EMA. Kept
|
|
40
|
+
// per-instance (not a function-local static) so a hot reload that destroys
|
|
41
|
+
// and recreates the bridge does not pollute the new bridge's first frame.
|
|
42
|
+
double _lastTickSec;
|
|
43
|
+
|
|
34
44
|
NSString* _cacheDir;
|
|
35
45
|
}
|
|
36
46
|
|
|
@@ -44,6 +54,8 @@
|
|
|
44
54
|
_viewportHeight = height;
|
|
45
55
|
_initialized = NO;
|
|
46
56
|
_suspended = NO;
|
|
57
|
+
_lastTickSec = 0.0;
|
|
58
|
+
_forceRender.store(true, std::memory_order_release);
|
|
47
59
|
_cacheDir = [cacheDir copy];
|
|
48
60
|
|
|
49
61
|
_config.cacheDatabasePath =
|
|
@@ -79,7 +91,7 @@
|
|
|
79
91
|
- (void)appWillResignActive:(NSNotification *)note { _suspended = YES; }
|
|
80
92
|
- (void)appDidBecomeActive:(NSNotification *)note {
|
|
81
93
|
_suspended = NO;
|
|
82
|
-
|
|
94
|
+
_forceRender.store(true, std::memory_order_release);
|
|
83
95
|
}
|
|
84
96
|
|
|
85
97
|
- (void)buildEngine {
|
|
@@ -87,9 +99,11 @@
|
|
|
87
99
|
_engine = std::make_unique<reactnativecesium::CesiumEngine>();
|
|
88
100
|
_engine->initialize(_config);
|
|
89
101
|
|
|
90
|
-
// Seed the
|
|
91
|
-
//
|
|
92
|
-
|
|
102
|
+
// Seed the integrator from the engine's initial camera (which itself was
|
|
103
|
+
// populated by GlobeCamera's defaults). This puts the demand and actual
|
|
104
|
+
// states in sync from the very first frame; no "snap from default" jump.
|
|
105
|
+
_integrator = std::make_unique<reactnativecesium::CameraIntegrator>(
|
|
106
|
+
_engine->camera().getParams());
|
|
93
107
|
|
|
94
108
|
auto* backendPtr = _metalBackend.get();
|
|
95
109
|
_engine->getResourcePreparer()->setGPUTextureCreator(
|
|
@@ -100,8 +114,6 @@
|
|
|
100
114
|
[](void* tex) {
|
|
101
115
|
if (tex) CFRelease(tex);
|
|
102
116
|
});
|
|
103
|
-
// Water mask textures reuse the same MTLTexture creator; the binding slot is
|
|
104
|
-
// set at draw time on Metal so no separate factory is needed.
|
|
105
117
|
_engine->getResourcePreparer()->setWaterMaskTextureCreator(
|
|
106
118
|
[backendPtr](const uint8_t* pixels, int32_t w, int32_t h) -> void* {
|
|
107
119
|
return backendPtr->createRasterTexture(pixels, w, h);
|
|
@@ -115,7 +127,7 @@
|
|
|
115
127
|
- (void)applyEngineConfig {
|
|
116
128
|
if (!_engine) return;
|
|
117
129
|
_engine->updateConfig(_config);
|
|
118
|
-
|
|
130
|
+
_forceRender.store(true, std::memory_order_release);
|
|
119
131
|
}
|
|
120
132
|
|
|
121
133
|
- (void)updateIonAccessToken:(NSString *)token assetId:(int64_t)assetId {
|
|
@@ -129,53 +141,93 @@
|
|
|
129
141
|
if (!_initialized) return;
|
|
130
142
|
_config.ionImageryAssetId = (assetId <= 0) ? 1 : assetId;
|
|
131
143
|
_engine->setImageryAssetId(_config.ionImageryAssetId);
|
|
132
|
-
|
|
144
|
+
_forceRender.store(true, std::memory_order_release);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ── Per-DoF setters ──────────────────────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
- (void)setPositionLatitude:(double)lat longitude:(double)lon {
|
|
150
|
+
if (!_initialized) return;
|
|
151
|
+
_integrator->setPosition(lat, lon);
|
|
152
|
+
_forceRender.store(true, std::memory_order_release);
|
|
133
153
|
}
|
|
134
154
|
|
|
135
|
-
- (void)
|
|
136
|
-
longitude:(double)lon
|
|
137
|
-
altitude:(double)alt
|
|
138
|
-
heading:(double)heading
|
|
139
|
-
pitch:(double)pitch
|
|
140
|
-
roll:(double)roll {
|
|
155
|
+
- (void)setAltitude:(double)alt {
|
|
141
156
|
if (!_initialized) return;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
altitude:(double)alt
|
|
148
|
-
heading:(double)heading
|
|
149
|
-
pitch:(double)pitch
|
|
150
|
-
roll:(double)roll
|
|
151
|
-
viewCorrectionW:(double)qw
|
|
152
|
-
x:(double)qx
|
|
153
|
-
y:(double)qy
|
|
154
|
-
z:(double)qz {
|
|
157
|
+
_integrator->setAltitude(alt);
|
|
158
|
+
_forceRender.store(true, std::memory_order_release);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
- (void)setHeadingDeg:(double)deg {
|
|
155
162
|
if (!_initialized) return;
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
163
|
+
_integrator->setHeading(deg);
|
|
164
|
+
_forceRender.store(true, std::memory_order_release);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
- (void)setAttitudePitch:(double)pitch roll:(double)roll {
|
|
168
|
+
if (!_initialized) return;
|
|
169
|
+
_integrator->setAttitude(pitch, roll);
|
|
170
|
+
_forceRender.store(true, std::memory_order_release);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
- (void)setViewCorrectionW:(double)qw x:(double)qx y:(double)qy z:(double)qz {
|
|
174
|
+
if (!_initialized) return;
|
|
175
|
+
_integrator->setViewCorrection(glm::dquat(qw, qx, qy, qz));
|
|
176
|
+
_forceRender.store(true, std::memory_order_release);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
- (void)teleportLatitude:(double)lat
|
|
180
|
+
longitude:(double)lon
|
|
181
|
+
altitude:(double)alt
|
|
182
|
+
heading:(double)heading
|
|
183
|
+
pitch:(double)pitch
|
|
184
|
+
roll:(double)roll
|
|
185
|
+
verticalFovDeg:(double)vfov {
|
|
186
|
+
if (!_initialized) return;
|
|
187
|
+
reactnativecesium::CameraParams p;
|
|
188
|
+
p.latitude = lat;
|
|
189
|
+
p.longitude = lon;
|
|
190
|
+
p.altitude = alt;
|
|
191
|
+
p.heading = heading;
|
|
192
|
+
p.pitch = pitch;
|
|
193
|
+
p.roll = roll;
|
|
194
|
+
p.verticalFov = vfov;
|
|
195
|
+
// Keep the latest viewCorrection target — teleport is a position/orientation
|
|
196
|
+
// jump, not a boresight reset. Use setViewCorrection(identity) to clear it.
|
|
197
|
+
p.viewCorrection = _integrator->getActual().viewCorrection;
|
|
198
|
+
_integrator->teleport(p);
|
|
199
|
+
// Apply directly to the engine so the next render uses the teleported value
|
|
200
|
+
// without one frame of "almost there" interpolation.
|
|
201
|
+
_engine->camera().setParams(p);
|
|
202
|
+
_forceRender.store(true, std::memory_order_release);
|
|
166
203
|
}
|
|
167
204
|
|
|
168
205
|
- (void)resize:(int)width height:(int)height {
|
|
169
206
|
_viewportWidth = width;
|
|
170
207
|
_viewportHeight = height;
|
|
171
208
|
if (_metalBackend) _metalBackend->resize(width, height);
|
|
172
|
-
|
|
209
|
+
_forceRender.store(true, std::memory_order_release);
|
|
173
210
|
}
|
|
174
211
|
|
|
175
212
|
- (void)setVerticalFovDeg:(double)degrees {
|
|
176
213
|
if (!_initialized) return;
|
|
177
|
-
|
|
178
|
-
|
|
214
|
+
_integrator->setVerticalFov(degrees);
|
|
215
|
+
_forceRender.store(true, std::memory_order_release);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
- (void)setRateCapsYaw:(double)yawDegSec
|
|
219
|
+
pitch:(double)pitchDegSec
|
|
220
|
+
roll:(double)rollDegSec
|
|
221
|
+
climb:(double)climbMps
|
|
222
|
+
groundSpeed:(double)groundMps {
|
|
223
|
+
if (!_initialized) return;
|
|
224
|
+
reactnativecesium::CameraRateCaps caps;
|
|
225
|
+
caps.maxYawRateDegSec = yawDegSec;
|
|
226
|
+
caps.maxPitchRateDegSec = pitchDegSec;
|
|
227
|
+
caps.maxRollRateDegSec = rollDegSec;
|
|
228
|
+
caps.maxClimbRateMps = climbMps;
|
|
229
|
+
caps.maxGroundSpeedMps = groundMps;
|
|
230
|
+
_integrator->setRateCaps(caps);
|
|
179
231
|
}
|
|
180
232
|
|
|
181
233
|
- (void)setMaximumScreenSpaceError:(double)v {
|
|
@@ -241,43 +293,35 @@
|
|
|
241
293
|
- (void)setMinAltitudeAboveTerrain:(float)v {
|
|
242
294
|
_config.minAltitudeAboveTerrain = std::max(0.0f, v);
|
|
243
295
|
if (_engine) _engine->updateConfig(_config);
|
|
244
|
-
|
|
296
|
+
_forceRender.store(true, std::memory_order_release);
|
|
245
297
|
}
|
|
246
298
|
|
|
247
299
|
- (void)setMsaaSampleCount:(int)samples {
|
|
248
300
|
if (_metalBackend) _metalBackend->setMsaaSampleCount(samples);
|
|
249
|
-
|
|
301
|
+
_forceRender.store(true, std::memory_order_release);
|
|
250
302
|
}
|
|
251
303
|
|
|
252
|
-
- (void)markNeedsRender {
|
|
304
|
+
- (void)markNeedsRender { _forceRender.store(true, std::memory_order_release); }
|
|
253
305
|
|
|
254
306
|
- (BOOL)shouldRenderNextFrame {
|
|
255
307
|
if (!_initialized || _suspended || !_engine) return NO;
|
|
256
|
-
if (
|
|
257
|
-
_target.requestForceRender(); // sticky for one frame so we make it through
|
|
258
|
-
return YES;
|
|
259
|
-
}
|
|
308
|
+
if (_forceRender.load(std::memory_order_acquire)) return YES;
|
|
260
309
|
if (_frameResult.tilesLoading > 0 || !_frameResult.tilesetActive) return YES;
|
|
261
|
-
if (
|
|
262
|
-
const auto cur = _engine->camera().getParams();
|
|
263
|
-
const auto tgt = _target.snapshot();
|
|
264
|
-
if (reactnativecesium::CameraTargetState::deltaExceedsEpsilon(cur, tgt)) {
|
|
265
|
-
return YES;
|
|
266
|
-
}
|
|
267
|
-
_target.clearDirty();
|
|
268
|
-
}
|
|
310
|
+
if (_integrator && _integrator->isActive()) return YES;
|
|
269
311
|
return NO;
|
|
270
312
|
}
|
|
271
313
|
|
|
272
|
-
- (void)
|
|
314
|
+
- (void)renderFrameAt:(double)nowSeconds {
|
|
273
315
|
if (!_initialized || _suspended) return;
|
|
316
|
+
if (!_engine || !_integrator || !_metalBackend) return;
|
|
274
317
|
|
|
275
318
|
@autoreleasepool {
|
|
276
|
-
(
|
|
319
|
+
// Consume the force-render flag (we're rendering this frame either way).
|
|
320
|
+
_forceRender.store(false, std::memory_order_release);
|
|
277
321
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
auto
|
|
322
|
+
// The integrator owns its own clock now — see CameraIntegrator::step().
|
|
323
|
+
// `nowSeconds` is still used below for the FPS-EMA dt only.
|
|
324
|
+
auto actual = _integrator->step();
|
|
281
325
|
|
|
282
326
|
// ── Terrain floor clamp ───────────────────────────────────────────────
|
|
283
327
|
// Uses terrain floor from the previous frame (one-frame lag is
|
|
@@ -285,23 +329,26 @@
|
|
|
285
329
|
const float minAbove = _engine->getConfig().minAltitudeAboveTerrain;
|
|
286
330
|
if (minAbove > 0.0f) {
|
|
287
331
|
const double geoidOffset =
|
|
288
|
-
reactnativecesium::mslToEllipsoidMeters(
|
|
332
|
+
reactnativecesium::mslToEllipsoidMeters(actual.latitude, actual.longitude, 0.0);
|
|
289
333
|
const double camEllipsoid =
|
|
290
|
-
reactnativecesium::mslToEllipsoidMeters(
|
|
334
|
+
reactnativecesium::mslToEllipsoidMeters(actual.latitude, actual.longitude, actual.altitude);
|
|
291
335
|
const double minEllipsoid =
|
|
292
336
|
static_cast<double>(_engine->terrainFloorEllipsoidMeters()) + minAbove;
|
|
293
337
|
if (camEllipsoid < minEllipsoid) {
|
|
294
|
-
|
|
338
|
+
const double clampedMsl = minEllipsoid - geoidOffset;
|
|
339
|
+
_integrator->clampActualAltitude(clampedMsl);
|
|
340
|
+
actual.altitude = clampedMsl;
|
|
295
341
|
}
|
|
296
342
|
}
|
|
297
343
|
|
|
298
|
-
_engine->camera().setParams(
|
|
299
|
-
|
|
300
|
-
if (!reactnativecesium::CameraTargetState::deltaExceedsEpsilon(smooth, tgt)) {
|
|
301
|
-
_target.clearDirty();
|
|
302
|
-
}
|
|
344
|
+
_engine->camera().setParams(actual);
|
|
303
345
|
|
|
304
346
|
_engine->updateFrame(_viewportWidth, _viewportHeight, _frameResult);
|
|
347
|
+
// For metrics purposes only — keeps the FPS EMA in something close to the
|
|
348
|
+
// wall-clock cadence. Not used by the integrator.
|
|
349
|
+
double dt = (_lastTickSec > 0.0) ? (nowSeconds - _lastTickSec) : (1.0 / 60.0);
|
|
350
|
+
_lastTickSec = nowSeconds;
|
|
351
|
+
if (dt > 0.5 || dt <= 0.0) dt = 1.0 / 60.0;
|
|
305
352
|
_metrics.tick(dt, _frameResult, !_config.tlsCaBundlePath.empty());
|
|
306
353
|
|
|
307
354
|
reactnativecesium::FrameParams params;
|
|
@@ -322,33 +369,72 @@
|
|
|
322
369
|
return [NSString stringWithUTF8String:_metrics.latest().creditsPlainText.c_str()];
|
|
323
370
|
}
|
|
324
371
|
|
|
325
|
-
|
|
326
|
-
- (double)
|
|
327
|
-
- (double)
|
|
328
|
-
- (double)
|
|
329
|
-
- (double)
|
|
330
|
-
- (double)
|
|
331
|
-
- (double)
|
|
372
|
+
// ── Actual camera readback (post-integration; what was rendered) ─────────
|
|
373
|
+
- (double)readActualLatitude { return _integrator ? _integrator->getActual().latitude : 0.0; }
|
|
374
|
+
- (double)readActualLongitude { return _integrator ? _integrator->getActual().longitude : 0.0; }
|
|
375
|
+
- (double)readActualAltitude { return _integrator ? _integrator->getActual().altitude : 0.0; }
|
|
376
|
+
- (double)readActualHeading { return _integrator ? _integrator->getActual().heading : 0.0; }
|
|
377
|
+
- (double)readActualPitch { return _integrator ? _integrator->getActual().pitch : 0.0; }
|
|
378
|
+
- (double)readActualRoll { return _integrator ? _integrator->getActual().roll : 0.0; }
|
|
379
|
+
- (double)readActualVerticalFovDeg { return _integrator ? _integrator->getActual().verticalFov : 60.0; }
|
|
380
|
+
|
|
381
|
+
// ── Demand camera readback (what the consumer last asked for) ────────────
|
|
382
|
+
- (double)readDemandLatitude { return _integrator ? _integrator->getDemand().latitude : 0.0; }
|
|
383
|
+
- (double)readDemandLongitude { return _integrator ? _integrator->getDemand().longitude : 0.0; }
|
|
384
|
+
- (double)readDemandAltitude { return _integrator ? _integrator->getDemand().altitude : 0.0; }
|
|
385
|
+
- (double)readDemandHeading { return _integrator ? _integrator->getDemand().heading : 0.0; }
|
|
386
|
+
- (double)readDemandPitch { return _integrator ? _integrator->getDemand().pitch : 0.0; }
|
|
387
|
+
- (double)readDemandRoll { return _integrator ? _integrator->getDemand().roll : 0.0; }
|
|
388
|
+
- (double)readDemandVerticalFovDeg { return _integrator ? _integrator->getDemand().verticalFov : 60.0; }
|
|
332
389
|
|
|
333
390
|
- (double)readViewCorrectionW {
|
|
334
|
-
|
|
335
|
-
return _engine->camera().getParams().viewCorrection.w;
|
|
391
|
+
return _integrator ? _integrator->getActual().viewCorrection.w : 1.0;
|
|
336
392
|
}
|
|
337
393
|
- (double)readViewCorrectionX {
|
|
338
|
-
return
|
|
394
|
+
return _integrator ? _integrator->getActual().viewCorrection.x : 0.0;
|
|
339
395
|
}
|
|
340
396
|
- (double)readViewCorrectionY {
|
|
341
|
-
return
|
|
397
|
+
return _integrator ? _integrator->getActual().viewCorrection.y : 0.0;
|
|
342
398
|
}
|
|
343
399
|
- (double)readViewCorrectionZ {
|
|
344
|
-
return
|
|
400
|
+
return _integrator ? _integrator->getActual().viewCorrection.z : 0.0;
|
|
345
401
|
}
|
|
346
402
|
|
|
347
403
|
- (void)shutdown {
|
|
348
404
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
405
|
+
|
|
406
|
+
// Mark uninitialised up-front so any late prop setters that race the
|
|
407
|
+
// teardown become no-ops instead of touching half-destroyed state.
|
|
408
|
+
_initialized = NO;
|
|
409
|
+
|
|
349
410
|
if (_engine) _engine->shutdown();
|
|
350
411
|
if (_metalBackend) _metalBackend->shutdown();
|
|
351
|
-
|
|
412
|
+
|
|
413
|
+
// Release the engine (and therefore the SqliteCache it owns) here rather
|
|
414
|
+
// than waiting for ARC to dealloc this CesiumBridge. During fast refresh
|
|
415
|
+
// the next CesiumView can mount and open the same cesium_cache.db file
|
|
416
|
+
// before our owning Swift view's deinit completes; freeing the unique_ptr
|
|
417
|
+
// now closes the SQLite handle synchronously and avoids "database is
|
|
418
|
+
// locked" warnings from the new engine's SqliteCache.
|
|
419
|
+
_integrator.reset();
|
|
420
|
+
_engine.reset();
|
|
421
|
+
_metalBackend.reset();
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
- (void)dealloc {
|
|
425
|
+
// Defensive: shutdown should already have run from the owning Swift view's
|
|
426
|
+
// deinit. If something exotic happened (e.g. a partially-built bridge that
|
|
427
|
+
// never had shutdown invoked, or an Objective-C exception path) we still
|
|
428
|
+
// need to detach from NSNotificationCenter and free the C++ owners in a
|
|
429
|
+
// deterministic order before ARC walks the ivar block.
|
|
430
|
+
if (_initialized) {
|
|
431
|
+
[self shutdown];
|
|
432
|
+
} else {
|
|
433
|
+
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
434
|
+
_integrator.reset();
|
|
435
|
+
_engine.reset();
|
|
436
|
+
_metalBackend.reset();
|
|
437
|
+
}
|
|
352
438
|
}
|
|
353
439
|
|
|
354
440
|
@end
|