react-native-webgpu 0.15.2 → 0.16.0
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/android/CMakeLists.txt +5 -0
- package/android/cpp/cpp-adapter.cpp +109 -11
- package/android/src/main/java/com/webgpu/SurfaceFrameRateVote.java +97 -85
- package/android/src/main/java/com/webgpu/WebGPUView.java +13 -8
- package/apple/ApplePlatformContext.h +1 -0
- package/cpp/jsi/JSICache.cpp +2 -0
- package/cpp/jsi/NativeObject.h +36 -12
- package/cpp/rnwgpu/RNWebGPUManager.cpp +4 -0
- package/cpp/rnwgpu/SurfaceRegistry.h +4 -4
- package/cpp/rnwgpu/api/GPUDevice.cpp +96 -0
- package/cpp/rnwgpu/api/GPUDevice.h +22 -0
- package/cpp/rnwgpu/api/GPUError.h +1 -0
- package/cpp/rnwgpu/api/GPUExtent2D.h +56 -0
- package/cpp/rnwgpu/api/GPUExternalTexture.cpp +32 -408
- package/cpp/rnwgpu/api/GPUExternalTexture.h +12 -37
- package/cpp/rnwgpu/api/GPUInternalError.h +1 -0
- package/cpp/rnwgpu/api/GPUNativeBufferExternalTexture.h +60 -0
- package/cpp/rnwgpu/api/GPUNativeBufferTexture.h +52 -0
- package/cpp/rnwgpu/api/GPUNativeBufferWrapper.cpp +144 -0
- package/cpp/rnwgpu/api/GPUNativeBufferWrapper.h +142 -0
- package/cpp/rnwgpu/api/GPUOutOfMemoryError.h +1 -0
- package/cpp/rnwgpu/api/GPUQueue.h +1 -0
- package/cpp/rnwgpu/api/GPUSharedFence.h +1 -0
- package/cpp/rnwgpu/api/GPUSharedTextureMemory.cpp +12 -44
- package/cpp/rnwgpu/api/GPUSharedTextureMemory.h +1 -0
- package/cpp/rnwgpu/api/GPUUncapturedErrorEvent.h +1 -0
- package/cpp/rnwgpu/api/GPUValidationError.h +1 -0
- package/cpp/rnwgpu/api/NativeFrameImport.cpp +638 -0
- package/cpp/rnwgpu/api/NativeFrameImport.h +86 -0
- package/cpp/rnwgpu/api/VideoFrame.h +21 -7
- package/cpp/rnwgpu/api/descriptors/GPUExternalTextureDescriptor.h +153 -1
- package/cpp/rnwgpu/api/descriptors/GPUNativeBufferTextureDescriptor.h +55 -0
- package/cpp/rnwgpu/async/RuntimeContext.cpp +1 -0
- package/lib/commonjs/Canvas.js.map +1 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/Canvas.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/Canvas.d.ts +2 -31
- package/lib/typescript/src/Canvas.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +4 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +33 -0
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/Canvas.tsx +3 -36
- package/src/index.tsx +24 -0
- package/src/types.ts +97 -0
package/android/CMakeLists.txt
CHANGED
|
@@ -46,6 +46,8 @@ add_library(${PACKAGE_NAME} SHARED
|
|
|
46
46
|
../cpp/rnwgpu/api/GPUSharedFence.cpp
|
|
47
47
|
../cpp/rnwgpu/api/GPUSharedTextureMemory.cpp
|
|
48
48
|
../cpp/rnwgpu/api/GPUExternalTexture.cpp
|
|
49
|
+
../cpp/rnwgpu/api/GPUNativeBufferWrapper.cpp
|
|
50
|
+
../cpp/rnwgpu/api/NativeFrameImport.cpp
|
|
49
51
|
../cpp/rnwgpu/api/GPURenderBundleEncoder.cpp
|
|
50
52
|
../cpp/rnwgpu/api/GPURenderPassEncoder.cpp
|
|
51
53
|
../cpp/rnwgpu/api/GPURenderPipeline.cpp
|
|
@@ -96,6 +98,8 @@ if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 76)
|
|
|
96
98
|
log
|
|
97
99
|
jnigraphics
|
|
98
100
|
android
|
|
101
|
+
# sync_merge, for the one fence endAccess() returns
|
|
102
|
+
sync
|
|
99
103
|
fbjni::fbjni
|
|
100
104
|
ReactAndroid::jsi
|
|
101
105
|
ReactAndroid::reactnative
|
|
@@ -108,6 +112,7 @@ else()
|
|
|
108
112
|
log
|
|
109
113
|
jnigraphics
|
|
110
114
|
android
|
|
115
|
+
sync
|
|
111
116
|
fbjni::fbjni
|
|
112
117
|
ReactAndroid::jsi
|
|
113
118
|
ReactAndroid::reactnativejni
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
#include <unistd.h>
|
|
2
2
|
|
|
3
|
+
#include <atomic>
|
|
4
|
+
#include <chrono>
|
|
5
|
+
#include <cstdint>
|
|
3
6
|
#include <cstring>
|
|
4
7
|
#include <memory>
|
|
8
|
+
#include <mutex>
|
|
5
9
|
#include <string>
|
|
6
10
|
#include <unordered_map>
|
|
7
11
|
#include <utility>
|
|
@@ -160,10 +164,10 @@ namespace {
|
|
|
160
164
|
|
|
161
165
|
// Weak handle on a Java view and one of its no-argument methods, for native
|
|
162
166
|
// threads to call into it: the fence waiter wakes a WebGPUHardwareBufferView
|
|
163
|
-
// when a frame is ready, the presenting thread
|
|
164
|
-
//
|
|
165
|
-
// is not kept alive by a SurfaceInfo that outlives it (the
|
|
166
|
-
// SurfaceInfo until onDropViewInstance).
|
|
167
|
+
// when a frame is ready, the presenting thread tells a SurfaceView it is
|
|
168
|
+
// animating (AnimationReporter). Holding the view weakly means a view that RN
|
|
169
|
+
// has dropped is not kept alive by a SurfaceInfo that outlives it (the
|
|
170
|
+
// registry keeps the SurfaceInfo until onDropViewInstance).
|
|
167
171
|
struct WeakViewCall {
|
|
168
172
|
jweak view = nullptr;
|
|
169
173
|
jmethodID method = nullptr;
|
|
@@ -203,11 +207,84 @@ struct WeakViewCall {
|
|
|
203
207
|
}
|
|
204
208
|
};
|
|
205
209
|
|
|
210
|
+
// Tells a SurfaceView it starts animating, for its refresh rate vote
|
|
211
|
+
// (WebGPUView.onAnimating), from the frames presented to its surface. A call
|
|
212
|
+
// into Java on the render path costs a JNI transition and attaches a presenting
|
|
213
|
+
// thread the JVM doesn't know for the length of the call, so there is one call
|
|
214
|
+
// per animation: the first frame presented right after its predecessor calls,
|
|
215
|
+
// the frames after it only record the time. The view polls that time from the
|
|
216
|
+
// main thread (releaseIfIdle) and, once the animation has stopped, takes its
|
|
217
|
+
// vote back and re-arms the call for the next animation. Lock-free, since it
|
|
218
|
+
// runs on every present.
|
|
219
|
+
class AnimationReporter {
|
|
220
|
+
public:
|
|
221
|
+
AnimationReporter(JNIEnv *env, jobject view)
|
|
222
|
+
: _onAnimating(env, view, "onAnimating") {}
|
|
223
|
+
|
|
224
|
+
void onFramePresented() {
|
|
225
|
+
const int64_t now = nowNanos();
|
|
226
|
+
const int64_t previous = _lastPresent.exchange(now);
|
|
227
|
+
if (now - previous > kAnimatingIntervalNanos) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
_lastAnimating.store(now);
|
|
231
|
+
if (!_reported.exchange(true)) {
|
|
232
|
+
_onAnimating();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Main thread. Returns how much longer the animation must stay stopped
|
|
237
|
+
// before it counts as idle, or 0 once it has: the next animating frame then
|
|
238
|
+
// calls into Java again.
|
|
239
|
+
int64_t releaseIfIdle(int64_t idleAfterNanos) {
|
|
240
|
+
const int64_t idle = nowNanos() - _lastAnimating.load();
|
|
241
|
+
if (idle < idleAfterNanos) {
|
|
242
|
+
return idleAfterNanos - idle;
|
|
243
|
+
}
|
|
244
|
+
_reported.store(false);
|
|
245
|
+
return 0;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private:
|
|
249
|
+
// Presents this close together are an animation (faster than 40 fps).
|
|
250
|
+
static constexpr int64_t kAnimatingIntervalNanos = 25'000'000;
|
|
251
|
+
|
|
252
|
+
static int64_t nowNanos() {
|
|
253
|
+
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
|
254
|
+
std::chrono::steady_clock::now().time_since_epoch())
|
|
255
|
+
.count();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
WeakViewCall _onAnimating;
|
|
259
|
+
// INT64_MIN / 2 rather than a sentinel: the first present is never "right
|
|
260
|
+
// after" it, and the subtractions above don't overflow.
|
|
261
|
+
std::atomic<int64_t> _lastPresent{INT64_MIN / 2};
|
|
262
|
+
std::atomic<int64_t> _lastAnimating{INT64_MIN / 2};
|
|
263
|
+
// Java has been told about the current animation.
|
|
264
|
+
std::atomic<bool> _reported{false};
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
// The reporters by contextId, for WebGPUView.releaseFrameRateVoteIfIdle. The
|
|
268
|
+
// surface callbacks and the idle check all run on the main thread; the lock
|
|
269
|
+
// only guards against a caller that doesn't.
|
|
270
|
+
std::mutex animationReportersMutex;
|
|
271
|
+
std::unordered_map<int, std::shared_ptr<AnimationReporter>> animationReporters;
|
|
272
|
+
|
|
273
|
+
void setAnimationReporter(int contextId,
|
|
274
|
+
std::shared_ptr<AnimationReporter> reporter) {
|
|
275
|
+
std::lock_guard<std::mutex> lock(animationReportersMutex);
|
|
276
|
+
if (reporter) {
|
|
277
|
+
animationReporters[contextId] = std::move(reporter);
|
|
278
|
+
} else {
|
|
279
|
+
animationReporters.erase(contextId);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
206
283
|
} // namespace
|
|
207
284
|
|
|
208
285
|
extern "C" JNIEXPORT void JNICALL Java_com_webgpu_WebGPUView_onSurfaceCreate(
|
|
209
286
|
JNIEnv *env, jobject thiz, jobject jSurface, jint contextId, jfloat width,
|
|
210
|
-
jfloat height, jboolean
|
|
287
|
+
jfloat height, jboolean reportAnimation) {
|
|
211
288
|
if (manager == nullptr) {
|
|
212
289
|
return;
|
|
213
290
|
}
|
|
@@ -228,13 +305,15 @@ extern "C" JNIEXPORT void JNICALL Java_com_webgpu_WebGPUView_onSurfaceCreate(
|
|
|
228
305
|
surface, [](void *nativeSurface) {
|
|
229
306
|
ANativeWindow_release(static_cast<ANativeWindow *>(nativeSurface));
|
|
230
307
|
});
|
|
231
|
-
// A SurfaceView asks to hear
|
|
232
|
-
//
|
|
233
|
-
if (
|
|
234
|
-
auto reporter =
|
|
235
|
-
|
|
236
|
-
info->setFramePresentedCallback(
|
|
308
|
+
// A SurfaceView asks to hear when it animates, for its refresh rate vote
|
|
309
|
+
// (WebGPUView.onAnimating).
|
|
310
|
+
if (reportAnimation) {
|
|
311
|
+
auto reporter = std::make_shared<AnimationReporter>(env, thiz);
|
|
312
|
+
setAnimationReporter(contextId, reporter);
|
|
313
|
+
info->setFramePresentedCallback(
|
|
314
|
+
[reporter]() { reporter->onFramePresented(); });
|
|
237
315
|
} else {
|
|
316
|
+
setAnimationReporter(contextId, nullptr);
|
|
238
317
|
info->setFramePresentedCallback(nullptr);
|
|
239
318
|
}
|
|
240
319
|
// The attach is adopted at the next frame boundary by the rendering thread;
|
|
@@ -246,6 +325,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_webgpu_WebGPUView_onSurfaceCreate(
|
|
|
246
325
|
extern "C" JNIEXPORT void JNICALL
|
|
247
326
|
Java_com_webgpu_WebGPUView_switchToOffscreenSurface(JNIEnv *env, jobject thiz,
|
|
248
327
|
jint contextId) {
|
|
328
|
+
setAnimationReporter(contextId, nullptr);
|
|
249
329
|
auto ®istry = rnwgpu::SurfaceRegistry::getInstance();
|
|
250
330
|
if (auto info = registry.getSurfaceInfo(contextId)) {
|
|
251
331
|
info->setFramePresentedCallback(nullptr);
|
|
@@ -253,12 +333,30 @@ Java_com_webgpu_WebGPUView_switchToOffscreenSurface(JNIEnv *env, jobject thiz,
|
|
|
253
333
|
}
|
|
254
334
|
}
|
|
255
335
|
|
|
336
|
+
// See AnimationReporter::releaseIfIdle; 0 when the surface reports nothing.
|
|
337
|
+
extern "C" JNIEXPORT jlong JNICALL
|
|
338
|
+
Java_com_webgpu_WebGPUView_releaseFrameRateVoteIfIdle(JNIEnv *env, jobject thiz,
|
|
339
|
+
jint contextId,
|
|
340
|
+
jlong idleAfterNanos) {
|
|
341
|
+
std::shared_ptr<AnimationReporter> reporter;
|
|
342
|
+
{
|
|
343
|
+
std::lock_guard<std::mutex> lock(animationReportersMutex);
|
|
344
|
+
auto it = animationReporters.find(contextId);
|
|
345
|
+
if (it == animationReporters.end()) {
|
|
346
|
+
return 0;
|
|
347
|
+
}
|
|
348
|
+
reporter = it->second;
|
|
349
|
+
}
|
|
350
|
+
return static_cast<jlong>(reporter->releaseIfIdle(idleAfterNanos));
|
|
351
|
+
}
|
|
352
|
+
|
|
256
353
|
extern "C" JNIEXPORT void JNICALL Java_com_webgpu_WebGPUView_onViewDestroyed(
|
|
257
354
|
JNIEnv *env, jobject thiz, jint contextId) {
|
|
258
355
|
// The view dies with its Canvas (contextIds are never reused), so view
|
|
259
356
|
// teardown retires the registry entry. The JS-side cleanup
|
|
260
357
|
// (RNWebGPU.destroyContext) only handles entries that never had a native
|
|
261
358
|
// surface; see RNWebGPU::destroyContext for the ownership split.
|
|
359
|
+
setAnimationReporter(contextId, nullptr);
|
|
262
360
|
auto ®istry = rnwgpu::SurfaceRegistry::getInstance();
|
|
263
361
|
if (auto info = registry.getSurfaceInfo(contextId)) {
|
|
264
362
|
info->setFramePresentedCallback(nullptr);
|
|
@@ -24,134 +24,146 @@ import androidx.annotation.Nullable;
|
|
|
24
24
|
* drops back to a few redraws a second: a layer with a vote counts as active
|
|
25
25
|
* for SurfaceFlinger even when it no longer draws.
|
|
26
26
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
27
|
+
* Native code tells onAnimating() once per animation, on the thread that
|
|
28
|
+
* presents (the JS thread, the Reanimated UI runtime or a worklet runtime),
|
|
29
|
+
* and the vote is cast right there, with no hop to a main thread that may be
|
|
30
|
+
* busy. The rate is resolved ahead of time on the main thread, where the
|
|
31
|
+
* view's display can be read. The idle check runs on the main thread and asks
|
|
32
|
+
* native code how long ago the canvas last animated; once it has stopped, it
|
|
33
|
+
* takes the vote back and native code calls onAnimating() again at the next
|
|
34
|
+
* animation. The lock only guards the state: the binder calls
|
|
35
|
+
* (Surface.setFrameRate) are made outside it.
|
|
30
36
|
*/
|
|
31
37
|
final class SurfaceFrameRateVote {
|
|
32
38
|
private static final String TAG = "SurfaceFrameRateVote";
|
|
33
39
|
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
// a
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
// The vote is taken back once the canvas has not animated for this long,
|
|
41
|
+
// whether it stops drawing or drops back to a few redraws a second. Native
|
|
42
|
+
// code decides what animating means (presents faster than 40 fps, see
|
|
43
|
+
// AnimationReporter in cpp-adapter.cpp). The threshold is absolute rather
|
|
44
|
+
// than relative to the current refresh rate, so that a canvas that only
|
|
45
|
+
// manages 60 fps at 120 Hz keeps its vote rather than oscillating between
|
|
46
|
+
// the two rates. A canvas that redraws a few times a second never votes.
|
|
47
|
+
static final long IDLE_AFTER_NANOS = 500_000_000L;
|
|
48
|
+
|
|
49
|
+
/** Native side of the vote, see AnimationReporter in cpp-adapter.cpp. */
|
|
50
|
+
interface AnimationSource {
|
|
51
|
+
/**
|
|
52
|
+
* How much longer the canvas must stay still before it counts as idle,
|
|
53
|
+
* or 0 once it does, in which case the next animation calls
|
|
54
|
+
* onAnimating() again. Main thread.
|
|
55
|
+
*/
|
|
56
|
+
long releaseIfIdle(long idleAfterNanos);
|
|
57
|
+
}
|
|
47
58
|
|
|
48
59
|
private final View mView;
|
|
60
|
+
private final AnimationSource mSource;
|
|
49
61
|
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
|
50
62
|
private final Runnable mIdleCheck = this::checkIdle;
|
|
51
63
|
|
|
52
64
|
@Nullable
|
|
53
65
|
private Surface mSurface;
|
|
54
66
|
private boolean mVoting = false;
|
|
55
|
-
|
|
56
|
-
//
|
|
57
|
-
private
|
|
58
|
-
// The last present that followed its predecessor within
|
|
59
|
-
// ANIMATING_INTERVAL_NANOS; 0 until the first.
|
|
60
|
-
private long mLastAnimatingNanos = 0;
|
|
67
|
+
// The rate to ask for, resolved on the main thread; 0 when unknown, in
|
|
68
|
+
// which case animations don't vote until the next idle check resolves it.
|
|
69
|
+
private float mRefreshRate = 0f;
|
|
61
70
|
|
|
62
71
|
/** The view the surface belongs to; its display decides the rate to ask for. */
|
|
63
|
-
SurfaceFrameRateVote(View view) {
|
|
72
|
+
SurfaceFrameRateVote(View view, AnimationSource source) {
|
|
64
73
|
mView = view;
|
|
74
|
+
mSource = source;
|
|
65
75
|
}
|
|
66
76
|
|
|
67
77
|
/** The surface was created: it is voted on until detach(). Main thread. */
|
|
68
|
-
|
|
78
|
+
void attach(Surface surface) {
|
|
69
79
|
detach();
|
|
70
80
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
|
71
81
|
return;
|
|
72
82
|
}
|
|
73
|
-
|
|
83
|
+
float refreshRate = resolveMaxRefreshRate();
|
|
84
|
+
synchronized (this) {
|
|
85
|
+
mSurface = surface;
|
|
86
|
+
mRefreshRate = refreshRate;
|
|
87
|
+
}
|
|
74
88
|
}
|
|
75
89
|
|
|
76
|
-
/**
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (!mVoting) {
|
|
87
|
-
vote();
|
|
90
|
+
/**
|
|
91
|
+
* The canvas started animating (presenting frames back to back). Called
|
|
92
|
+
* once per animation, on the presenting thread.
|
|
93
|
+
*/
|
|
94
|
+
void onAnimating() {
|
|
95
|
+
Surface surface;
|
|
96
|
+
float refreshRate;
|
|
97
|
+
synchronized (this) {
|
|
98
|
+
if (mSurface == null) {
|
|
99
|
+
return;
|
|
88
100
|
}
|
|
101
|
+
surface = mSurface;
|
|
102
|
+
refreshRate = mRefreshRate;
|
|
103
|
+
mVoting = refreshRate > 0;
|
|
89
104
|
}
|
|
90
|
-
|
|
91
|
-
|
|
105
|
+
// Whether it votes or not, the idle check re-arms native code once the
|
|
106
|
+
// animation stops.
|
|
107
|
+
mHandler.postDelayed(mIdleCheck, IDLE_AFTER_NANOS / 1_000_000);
|
|
108
|
+
if (refreshRate > 0) {
|
|
109
|
+
// This form only switches when the display can do so seamlessly, like
|
|
110
|
+
// the vote the window casts for its own views: a canvas animation is
|
|
111
|
+
// not worth a blank screen. Surface.setFrameRate is thread-safe.
|
|
112
|
+
setFrameRate(surface, refreshRate);
|
|
92
113
|
}
|
|
93
114
|
}
|
|
94
115
|
|
|
95
116
|
/** The surface is going away (it is still valid here) or the view left its window. Main thread. */
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
117
|
+
void detach() {
|
|
118
|
+
Surface surface;
|
|
119
|
+
boolean voting;
|
|
120
|
+
synchronized (this) {
|
|
121
|
+
surface = mSurface;
|
|
122
|
+
voting = mVoting;
|
|
123
|
+
mSurface = null;
|
|
124
|
+
mVoting = false;
|
|
99
125
|
}
|
|
100
|
-
withdraw();
|
|
101
126
|
mHandler.removeCallbacks(mIdleCheck);
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
private void vote() {
|
|
109
|
-
float refreshRate = resolveMaxRefreshRate();
|
|
110
|
-
if (refreshRate <= 0 || !mSurface.isValid()) {
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
try {
|
|
114
|
-
// This form only switches when the display can do so seamlessly, like
|
|
115
|
-
// the vote the window casts for its own views: a canvas animation is
|
|
116
|
-
// not worth a blank screen. Surface.setFrameRate is thread-safe.
|
|
117
|
-
mSurface.setFrameRate(refreshRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
|
|
118
|
-
mVoting = true;
|
|
119
|
-
} catch (RuntimeException e) {
|
|
120
|
-
Log.w(TAG, "Could not vote the refresh rate, leaving it to the display", e);
|
|
121
|
-
detach();
|
|
127
|
+
// A vote cast concurrently on the presenting thread may land after this;
|
|
128
|
+
// it dies with the surface, which is going away.
|
|
129
|
+
if (surface != null && voting) {
|
|
130
|
+
setFrameRate(surface, 0);
|
|
122
131
|
}
|
|
123
132
|
}
|
|
124
133
|
|
|
125
|
-
private void
|
|
126
|
-
|
|
134
|
+
private void checkIdle() {
|
|
135
|
+
long remainingNanos = mSource.releaseIfIdle(IDLE_AFTER_NANOS);
|
|
136
|
+
if (remainingNanos > 0) {
|
|
137
|
+
mHandler.postDelayed(mIdleCheck, (remainingNanos + 999_999) / 1_000_000);
|
|
127
138
|
return;
|
|
128
139
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
140
|
+
// Native code is re-armed: the next onAnimating() can only come after
|
|
141
|
+
// this point, so it doesn't race with the withdrawal below.
|
|
142
|
+
float refreshRate = resolveMaxRefreshRate();
|
|
143
|
+
Surface surface;
|
|
144
|
+
boolean voting;
|
|
145
|
+
synchronized (this) {
|
|
146
|
+
surface = mSurface;
|
|
147
|
+
voting = mVoting;
|
|
148
|
+
mVoting = false;
|
|
149
|
+
if (surface != null) {
|
|
150
|
+
// Follows the display the view is on, for the next animation.
|
|
151
|
+
mRefreshRate = refreshRate;
|
|
152
|
+
}
|
|
132
153
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
} catch (RuntimeException e) {
|
|
136
|
-
Log.w(TAG, "Could not withdraw the refresh rate vote", e);
|
|
154
|
+
if (surface != null && voting) {
|
|
155
|
+
setFrameRate(surface, 0);
|
|
137
156
|
}
|
|
138
157
|
}
|
|
139
158
|
|
|
140
|
-
private void
|
|
141
|
-
|
|
142
|
-
mHandler.postDelayed(mIdleCheck, (delayNanos + 999_999) / 1_000_000);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
private synchronized void checkIdle() {
|
|
146
|
-
mIdleCheckPending = false;
|
|
147
|
-
if (!mVoting) {
|
|
159
|
+
private static void setFrameRate(Surface surface, float refreshRate) {
|
|
160
|
+
if (!surface.isValid()) {
|
|
148
161
|
return;
|
|
149
162
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
scheduleIdleCheck(IDLE_AFTER_NANOS - idleNanos);
|
|
163
|
+
try {
|
|
164
|
+
surface.setFrameRate(refreshRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
|
|
165
|
+
} catch (RuntimeException e) {
|
|
166
|
+
Log.w(TAG, "Could not set the surface's frame rate, leaving it to the display", e);
|
|
155
167
|
}
|
|
156
168
|
}
|
|
157
169
|
|
|
@@ -26,7 +26,8 @@ public class WebGPUView extends ReactViewGroup implements WebGPUAPI {
|
|
|
26
26
|
private View mView = null;
|
|
27
27
|
// Only a SurfaceView votes: a TextureView or HardwareBufferView is
|
|
28
28
|
// composited into the window, which votes for it.
|
|
29
|
-
private final SurfaceFrameRateVote mFrameRateVote = new SurfaceFrameRateVote(
|
|
29
|
+
private final SurfaceFrameRateVote mFrameRateVote = new SurfaceFrameRateVote(
|
|
30
|
+
this, idleAfterNanos -> releaseFrameRateVoteIfIdle(mContextId, idleAfterNanos));
|
|
30
31
|
|
|
31
32
|
WebGPUView(Context context) {
|
|
32
33
|
super(context);
|
|
@@ -160,12 +161,12 @@ public class WebGPUView extends ReactViewGroup implements WebGPUAPI {
|
|
|
160
161
|
}
|
|
161
162
|
|
|
162
163
|
/**
|
|
163
|
-
* Called from native (
|
|
164
|
-
*
|
|
164
|
+
* Called from native (AnimationReporter in cpp-adapter.cpp) on the thread
|
|
165
|
+
* that presents to the surface, once per animation, only for a SurfaceView.
|
|
165
166
|
*/
|
|
166
167
|
@DoNotStrip
|
|
167
|
-
void
|
|
168
|
-
mFrameRateVote.
|
|
168
|
+
void onAnimating() {
|
|
169
|
+
mFrameRateVote.onAnimating();
|
|
169
170
|
}
|
|
170
171
|
|
|
171
172
|
@Override
|
|
@@ -182,15 +183,15 @@ public class WebGPUView extends ReactViewGroup implements WebGPUAPI {
|
|
|
182
183
|
onViewDestroyed(mContextId);
|
|
183
184
|
}
|
|
184
185
|
|
|
185
|
-
//
|
|
186
|
-
// presented to this surface.
|
|
186
|
+
// reportAnimation asks native to call onAnimating() when frames start being
|
|
187
|
+
// presented to this surface back to back.
|
|
187
188
|
@DoNotStrip
|
|
188
189
|
private native void onSurfaceCreate(
|
|
189
190
|
Surface surface,
|
|
190
191
|
int contextId,
|
|
191
192
|
float width,
|
|
192
193
|
float height,
|
|
193
|
-
boolean
|
|
194
|
+
boolean reportAnimation
|
|
194
195
|
);
|
|
195
196
|
|
|
196
197
|
@DoNotStrip
|
|
@@ -207,4 +208,8 @@ public class WebGPUView extends ReactViewGroup implements WebGPUAPI {
|
|
|
207
208
|
@DoNotStrip
|
|
208
209
|
private native void onViewDestroyed(int contextId);
|
|
209
210
|
|
|
211
|
+
// See SurfaceFrameRateVote.AnimationSource.
|
|
212
|
+
@DoNotStrip
|
|
213
|
+
private native long releaseFrameRateVoteIfIdle(int contextId, long idleAfterNanos);
|
|
214
|
+
|
|
210
215
|
}
|
package/cpp/jsi/JSICache.cpp
CHANGED
package/cpp/jsi/NativeObject.h
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
#include <typeindex>
|
|
15
15
|
#include <unordered_map>
|
|
16
16
|
#include <utility>
|
|
17
|
+
#include <vector>
|
|
17
18
|
|
|
18
19
|
#include "EnforceRange.h"
|
|
19
20
|
#include "JSICache.h"
|
|
@@ -640,19 +641,26 @@ protected:
|
|
|
640
641
|
rt, std::move(result));
|
|
641
642
|
}
|
|
642
643
|
});
|
|
644
|
+
defineGetter(runtime, prototype, name, getterFunc);
|
|
645
|
+
}
|
|
643
646
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
647
|
+
/**
|
|
648
|
+
* Install a getter whose native implementation builds the JS value itself,
|
|
649
|
+
* from the calling jsi::Runtime: for values no JSIConverter produces (null
|
|
650
|
+
* or a BigInt, say).
|
|
651
|
+
*/
|
|
652
|
+
static void installGetter(jsi::Runtime &runtime, jsi::Object &prototype,
|
|
653
|
+
const char *name,
|
|
654
|
+
jsi::Value (Derived::*getter)(jsi::Runtime &)) {
|
|
655
|
+
auto getterFunc = jsi::Function::createFromHostFunction(
|
|
656
|
+
runtime, jsi::PropNameID::forUtf8(runtime, std::string("get_") + name),
|
|
657
|
+
0,
|
|
658
|
+
[getter](jsi::Runtime &rt, const jsi::Value &thisVal,
|
|
659
|
+
const jsi::Value *args, size_t count) -> jsi::Value {
|
|
660
|
+
auto native = Derived::fromValue(rt, thisVal);
|
|
661
|
+
return (native.get()->*getter)(rt);
|
|
662
|
+
});
|
|
663
|
+
defineGetter(runtime, prototype, name, getterFunc);
|
|
656
664
|
}
|
|
657
665
|
|
|
658
666
|
/**
|
|
@@ -757,6 +765,22 @@ protected:
|
|
|
757
765
|
}
|
|
758
766
|
|
|
759
767
|
private:
|
|
768
|
+
// Use Object.defineProperty to create a proper getter
|
|
769
|
+
static void defineGetter(jsi::Runtime &runtime, jsi::Object &prototype,
|
|
770
|
+
const char *name, const jsi::Function &getterFunc) {
|
|
771
|
+
auto objectCtor = runtime.global().getPropertyAsObject(runtime, "Object");
|
|
772
|
+
auto defineProperty =
|
|
773
|
+
objectCtor.getPropertyAsFunction(runtime, "defineProperty");
|
|
774
|
+
|
|
775
|
+
jsi::Object descriptor(runtime);
|
|
776
|
+
descriptor.setProperty(runtime, "get", getterFunc);
|
|
777
|
+
descriptor.setProperty(runtime, "enumerable", true);
|
|
778
|
+
descriptor.setProperty(runtime, "configurable", true);
|
|
779
|
+
|
|
780
|
+
defineProperty.call(runtime, prototype,
|
|
781
|
+
jsi::String::createFromUtf8(runtime, name), descriptor);
|
|
782
|
+
}
|
|
783
|
+
|
|
760
784
|
// Helper to call a method that takes the calling jsi::Runtime as its first
|
|
761
785
|
// parameter, with JSI argument conversion for the rest and JSI conversion of
|
|
762
786
|
// the result.
|
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
#include "GPUError.h"
|
|
23
23
|
#include "GPUExternalTexture.h"
|
|
24
24
|
#include "GPUInternalError.h"
|
|
25
|
+
#include "GPUNativeBufferExternalTexture.h"
|
|
26
|
+
#include "GPUNativeBufferTexture.h"
|
|
25
27
|
#include "GPUOutOfMemoryError.h"
|
|
26
28
|
#include "GPUPipelineLayout.h"
|
|
27
29
|
#include "GPUQuerySet.h"
|
|
@@ -102,6 +104,8 @@ RNWebGPUManager::RNWebGPUManager(
|
|
|
102
104
|
GPUDeviceLostInfo::installConstructor(*_jsRuntime);
|
|
103
105
|
GPUError::installConstructor(*_jsRuntime);
|
|
104
106
|
GPUExternalTexture::installConstructor(*_jsRuntime);
|
|
107
|
+
GPUNativeBufferExternalTexture::installConstructor(*_jsRuntime);
|
|
108
|
+
GPUNativeBufferTexture::installConstructor(*_jsRuntime);
|
|
105
109
|
GPUInternalError::installConstructor(*_jsRuntime);
|
|
106
110
|
GPUOutOfMemoryError::installConstructor(*_jsRuntime);
|
|
107
111
|
GPUValidationError::installConstructor(*_jsRuntime);
|
|
@@ -59,7 +59,7 @@ struct Size {
|
|
|
59
59
|
using NativeSurfaceReleaser = std::function<void(void *)>;
|
|
60
60
|
|
|
61
61
|
#if defined(__ANDROID__)
|
|
62
|
-
// --- AHB-pool presentation mode (
|
|
62
|
+
// --- AHB-pool presentation mode (WebGPUNativeBufferView)
|
|
63
63
|
// -----------------------------
|
|
64
64
|
//
|
|
65
65
|
// A third presentation backend (besides the on-screen wgpu::Surface swapchain
|
|
@@ -71,7 +71,7 @@ using NativeSurfaceReleaser = std::function<void(void *)>;
|
|
|
71
71
|
// like the swapchain, so the canvas texture always matches the app's other
|
|
72
72
|
// attachments (e.g. its depth texture).
|
|
73
73
|
|
|
74
|
-
#define RNWGPU_LOG_TAG "
|
|
74
|
+
#define RNWGPU_LOG_TAG "WebGPUNativeBufferView"
|
|
75
75
|
// Failure / anomaly reporting only (never per-frame).
|
|
76
76
|
#define RNWGPU_POOL_WARN(...) \
|
|
77
77
|
__android_log_print(ANDROID_LOG_WARN, RNWGPU_LOG_TAG, __VA_ARGS__)
|
|
@@ -435,7 +435,7 @@ public:
|
|
|
435
435
|
bool hasNativeSurface() {
|
|
436
436
|
std::shared_lock<std::shared_mutex> lock(_mutex);
|
|
437
437
|
#if defined(__ANDROID__)
|
|
438
|
-
// A
|
|
438
|
+
// A WebGPUNativeBufferView in pool mode owns presentation the same way a
|
|
439
439
|
// surface-backed view does, and retires the entry on its own teardown.
|
|
440
440
|
if (_poolMode.load()) {
|
|
441
441
|
return true;
|
|
@@ -725,7 +725,7 @@ private:
|
|
|
725
725
|
{
|
|
726
726
|
std::unique_lock<std::shared_mutex> lock(_mutex);
|
|
727
727
|
#if defined(__ANDROID__)
|
|
728
|
-
// Leaving pool mode (the
|
|
728
|
+
// Leaving pool mode (the WebGPUNativeBufferView detached): drop the
|
|
729
729
|
// pool. A configured context keeps rendering into the offscreen
|
|
730
730
|
// fallback, exactly like the surface path.
|
|
731
731
|
if (_poolMode.load()) {
|