react-native-webgpu 0.10.5 → 0.11.1
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/cpp/AndroidPlatformContext.h +100 -9
- package/android/cpp/AndroidVideoPlayer.h +307 -0
- package/android/cpp/cpp-adapter.cpp +66 -6
- package/android/src/main/java/com/webgpu/ViewSnapshot.java +162 -0
- package/android/src/main/java/com/webgpu/WebGPUModule.java +78 -0
- package/android/src/main/java/com/webgpu/WebGPUVideoPlayer.java +420 -0
- package/apple/ApplePlatformContext.h +14 -1
- package/apple/ApplePlatformContext.mm +176 -1
- package/apple/AppleVideoPlayer.mm +157 -47
- package/apple/WebGPUModule.mm +20 -1
- package/cpp/rnwgpu/PlatformContext.h +76 -1
- package/cpp/rnwgpu/RNWebGPUManager.cpp +7 -0
- package/cpp/rnwgpu/api/GPUDevice.cpp +2 -1
- package/cpp/rnwgpu/api/GPUDevice.h +6 -0
- package/cpp/rnwgpu/api/GPUQueue.cpp +261 -4
- package/cpp/rnwgpu/api/GPUQueue.h +29 -2
- package/cpp/rnwgpu/api/VideoFrameBlit.h +355 -0
- package/cpp/rnwgpu/api/VideoPlayer.h +50 -3
- package/cpp/rnwgpu/api/descriptors/GPUDrawElementImageDestination.h +60 -0
- package/cpp/rnwgpu/api/descriptors/GPUDrawElementImageSource.h +106 -0
- package/cpp/rnwgpu/api/descriptors/GPUImageCopyExternalImage.h +30 -3
- package/lib/commonjs/importDevice.js +2 -2
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/main/index.js +45 -0
- package/lib/commonjs/main/index.js.map +1 -1
- package/lib/commonjs/main/index.web.js +11 -0
- package/lib/commonjs/main/index.web.js.map +1 -1
- package/lib/commonjs/video.js +58 -0
- package/lib/commonjs/video.js.map +1 -0
- package/lib/module/importDevice.js +2 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/main/index.js +34 -0
- package/lib/module/main/index.js.map +1 -1
- package/lib/module/main/index.web.js +1 -0
- package/lib/module/main/index.web.js.map +1 -1
- package/lib/module/video.js +49 -0
- package/lib/module/video.js.map +1 -0
- package/lib/typescript/lib/commonjs/video.d.ts +30 -0
- package/lib/typescript/lib/commonjs/video.d.ts.map +1 -0
- package/lib/typescript/lib/module/main/index.d.ts +1 -0
- package/lib/typescript/lib/module/main/index.web.d.ts +1 -0
- package/lib/typescript/lib/module/video.d.ts +4 -0
- package/lib/typescript/lib/module/video.d.ts.map +1 -0
- package/lib/typescript/src/importDevice.d.ts +2 -2
- package/lib/typescript/src/index.d.ts +8 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/main/index.d.ts +1 -0
- package/lib/typescript/src/main/index.d.ts.map +1 -1
- package/lib/typescript/src/main/index.web.d.ts +1 -0
- package/lib/typescript/src/main/index.web.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +32 -0
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/video.d.ts +41 -0
- package/lib/typescript/src/video.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/importDevice.ts +2 -2
- package/src/index.tsx +57 -3
- package/src/main/index.tsx +50 -0
- package/src/main/index.web.tsx +1 -0
- package/src/types.ts +89 -7
- package/src/video.ts +69 -0
|
@@ -67,6 +67,10 @@ RNWebGPUManager::RNWebGPUManager(
|
|
|
67
67
|
// no invoker (best-effort; see README "Threading model").
|
|
68
68
|
async::RuntimeContext::registerMainRuntime(_jsRuntime, _jsCallInvoker);
|
|
69
69
|
|
|
70
|
+
// Expose the platform context to API objects created later without a
|
|
71
|
+
// reference to it (GPUQueue::drawElementImageToTexture).
|
|
72
|
+
PlatformContext::setCurrent(_platformContext);
|
|
73
|
+
|
|
70
74
|
auto gpu = std::make_shared<GPU>(*_jsRuntime);
|
|
71
75
|
auto rnWebGPU =
|
|
72
76
|
std::make_shared<RNWebGPU>(gpu, _platformContext, _jsCallInvoker);
|
|
@@ -248,6 +252,9 @@ RNWebGPUManager::~RNWebGPUManager() {
|
|
|
248
252
|
// contextId counter, and stale entries would alias new canvases onto dead
|
|
249
253
|
// surfaces.
|
|
250
254
|
SurfaceRegistry::getInstance().clear();
|
|
255
|
+
if (PlatformContext::current() == _platformContext) {
|
|
256
|
+
PlatformContext::setCurrent(nullptr);
|
|
257
|
+
}
|
|
251
258
|
_jsRuntime = nullptr;
|
|
252
259
|
_jsCallInvoker = nullptr;
|
|
253
260
|
}
|
|
@@ -133,7 +133,8 @@ std::shared_ptr<GPUSupportedLimits> GPUDevice::getLimits() {
|
|
|
133
133
|
|
|
134
134
|
std::shared_ptr<GPUQueue> GPUDevice::getQueue() {
|
|
135
135
|
auto result = _instance.GetQueue();
|
|
136
|
-
return std::make_shared<GPUQueue>(result, _async,
|
|
136
|
+
return std::make_shared<GPUQueue>(_instance, result, _async, _videoFrameBlit,
|
|
137
|
+
_label);
|
|
137
138
|
}
|
|
138
139
|
|
|
139
140
|
std::shared_ptr<GPUCommandEncoder> GPUDevice::createCommandEncoder(
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
#include "GPUTexture.h"
|
|
55
55
|
#include "GPUTextureDescriptor.h"
|
|
56
56
|
#include "GPUUncapturedErrorEvent.h"
|
|
57
|
+
#include "VideoFrameBlit.h"
|
|
57
58
|
|
|
58
59
|
namespace rnwgpu {
|
|
59
60
|
|
|
@@ -285,6 +286,11 @@ private:
|
|
|
285
286
|
wgpu::Device _instance;
|
|
286
287
|
std::shared_ptr<async::RuntimeContext> _async;
|
|
287
288
|
std::string _label;
|
|
289
|
+
// Renders native video frames into textures for
|
|
290
|
+
// GPUQueue.copyExternalImageToTexture; shared by every GPUQueue wrapper of
|
|
291
|
+
// this device so the pipelines are created once.
|
|
292
|
+
std::shared_ptr<VideoFrameBlit> _videoFrameBlit =
|
|
293
|
+
std::make_shared<VideoFrameBlit>();
|
|
288
294
|
// Guards the device-lost state below. getLost() runs on a JS thread, but
|
|
289
295
|
// Dawn's AllowSpontaneous device-lost callback (and device destruction) can
|
|
290
296
|
// fire notifyDeviceLost() from other threads, so the mutex keeps these
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
#include "GPUQueue.h"
|
|
2
2
|
|
|
3
|
+
#include <algorithm>
|
|
4
|
+
#include <cmath>
|
|
3
5
|
#include <limits>
|
|
4
6
|
#include <memory>
|
|
7
|
+
#include <string>
|
|
8
|
+
#include <utility>
|
|
5
9
|
#include <vector>
|
|
6
10
|
|
|
7
11
|
#include "Convertors.h"
|
|
12
|
+
#include "ImageBitmap.h"
|
|
13
|
+
#include "PlatformContext.h"
|
|
8
14
|
|
|
9
15
|
namespace rnwgpu {
|
|
10
16
|
|
|
@@ -103,6 +109,68 @@ async::AsyncTaskHandle GPUQueue::onSubmittedWorkDone(jsi::Runtime &runtime) {
|
|
|
103
109
|
});
|
|
104
110
|
}
|
|
105
111
|
|
|
112
|
+
void GPUQueue::copyVideoFrameToTexture(
|
|
113
|
+
const GPUImageCopyExternalImage &source,
|
|
114
|
+
const GPUImageCopyTextureTagged &destination, const wgpu::Extent3D &size) {
|
|
115
|
+
if (!destination.texture) {
|
|
116
|
+
throw std::runtime_error(
|
|
117
|
+
"copyExternalImageToTexture: destination.texture is required");
|
|
118
|
+
}
|
|
119
|
+
if (destination.aspect.value_or(wgpu::TextureAspect::All) !=
|
|
120
|
+
wgpu::TextureAspect::All) {
|
|
121
|
+
throw std::runtime_error(
|
|
122
|
+
"copyExternalImageToTexture: a native frame can only be copied into "
|
|
123
|
+
"the color aspect");
|
|
124
|
+
}
|
|
125
|
+
if (size.depthOrArrayLayers > 1) {
|
|
126
|
+
throw std::runtime_error(
|
|
127
|
+
"copyExternalImageToTexture: a native frame is a single 2D image");
|
|
128
|
+
}
|
|
129
|
+
constexpr double kMaxCoordinate =
|
|
130
|
+
static_cast<double>(std::numeric_limits<uint32_t>::max());
|
|
131
|
+
auto coordinate = [&](double value, const char *name) {
|
|
132
|
+
if (!(value >= 0) || value > kMaxCoordinate) {
|
|
133
|
+
throw std::runtime_error(std::string("copyExternalImageToTexture: ") +
|
|
134
|
+
name +
|
|
135
|
+
" must be a non-negative integer coordinate");
|
|
136
|
+
}
|
|
137
|
+
return static_cast<uint32_t>(value);
|
|
138
|
+
};
|
|
139
|
+
const double rotation = source.rotation.value_or(0);
|
|
140
|
+
if (rotation != 0 && rotation != 90 && rotation != 180 && rotation != 270) {
|
|
141
|
+
throw std::runtime_error(
|
|
142
|
+
"copyExternalImageToTexture: rotation must be 0, 90, 180 or 270");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
VideoFrameBlit::Request request;
|
|
146
|
+
request.frame = source.videoFrame;
|
|
147
|
+
if (const auto origin = source.origin.value_or(nullptr)) {
|
|
148
|
+
request.originX = coordinate(origin->x, "origin.x");
|
|
149
|
+
request.originY = coordinate(origin->y, "origin.y");
|
|
150
|
+
}
|
|
151
|
+
request.width = size.width;
|
|
152
|
+
request.height = size.height;
|
|
153
|
+
request.flipY = source.flipY.value_or(false);
|
|
154
|
+
request.rotation = rotation;
|
|
155
|
+
request.mirrored = source.mirrored.value_or(false);
|
|
156
|
+
request.texture = destination.texture->get();
|
|
157
|
+
request.mipLevel = coordinate(destination.mipLevel.value_or(0), "mipLevel");
|
|
158
|
+
wgpu::Origin3D dstOrigin{};
|
|
159
|
+
Convertor conv;
|
|
160
|
+
if (!conv(dstOrigin, destination.origin)) {
|
|
161
|
+
throw std::runtime_error(
|
|
162
|
+
"copyExternalImageToTexture: invalid destination origin");
|
|
163
|
+
}
|
|
164
|
+
if (dstOrigin.z != 0) {
|
|
165
|
+
throw std::runtime_error(
|
|
166
|
+
"copyExternalImageToTexture: a native frame can only be copied into "
|
|
167
|
+
"the first layer of the texture");
|
|
168
|
+
}
|
|
169
|
+
request.dstX = dstOrigin.x;
|
|
170
|
+
request.dstY = dstOrigin.y;
|
|
171
|
+
_videoFrameBlit->copy(_device, _instance, request);
|
|
172
|
+
}
|
|
173
|
+
|
|
106
174
|
void GPUQueue::copyExternalImageToTexture(
|
|
107
175
|
std::shared_ptr<GPUImageCopyExternalImage> source,
|
|
108
176
|
std::shared_ptr<GPUImageCopyTextureTagged> destination,
|
|
@@ -111,6 +179,17 @@ void GPUQueue::copyExternalImageToTexture(
|
|
|
111
179
|
wgpu::TexelCopyBufferLayout layout{};
|
|
112
180
|
wgpu::Extent3D sz{};
|
|
113
181
|
Convertor conv;
|
|
182
|
+
if (source->videoFrame) {
|
|
183
|
+
if (!conv(sz, size)) {
|
|
184
|
+
throw std::runtime_error("copyExternalImageToTexture: invalid copy size");
|
|
185
|
+
}
|
|
186
|
+
copyVideoFrameToTexture(*source, *destination, sz);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (!source->source) {
|
|
190
|
+
throw std::runtime_error("copyExternalImageToTexture: the source must be "
|
|
191
|
+
"an ImageBitmap or a NativeVideoFrame");
|
|
192
|
+
}
|
|
114
193
|
uint32_t bytesPerPixel =
|
|
115
194
|
source->source->getSize() /
|
|
116
195
|
(source->source->getWidth() * source->source->getHeight());
|
|
@@ -139,10 +218,8 @@ void GPUQueue::copyExternalImageToTexture(
|
|
|
139
218
|
throw std::runtime_error(
|
|
140
219
|
"The source origin must be a non-negative integer coordinate.");
|
|
141
220
|
}
|
|
142
|
-
const size_t sourceOriginX =
|
|
143
|
-
|
|
144
|
-
const size_t sourceOriginY =
|
|
145
|
-
origin ? static_cast<size_t>(origin->y) : 0;
|
|
221
|
+
const size_t sourceOriginX = origin ? static_cast<size_t>(origin->x) : 0;
|
|
222
|
+
const size_t sourceOriginY = origin ? static_cast<size_t>(origin->y) : 0;
|
|
146
223
|
if (sourceOriginX > source->source->getWidth() ||
|
|
147
224
|
sz.width > source->source->getWidth() - sourceOriginX ||
|
|
148
225
|
sourceOriginY > source->source->getHeight() ||
|
|
@@ -193,6 +270,186 @@ void GPUQueue::copyExternalImageToTexture(
|
|
|
193
270
|
}
|
|
194
271
|
}
|
|
195
272
|
|
|
273
|
+
async::AsyncTaskHandle GPUQueue::drawElementImageToTexture(
|
|
274
|
+
jsi::Runtime &runtime, std::shared_ptr<GPUDrawElementImageSource> source,
|
|
275
|
+
std::shared_ptr<GPUDrawElementImageDestination> destination) {
|
|
276
|
+
auto platform = PlatformContext::current();
|
|
277
|
+
if (!platform) {
|
|
278
|
+
throw std::runtime_error(
|
|
279
|
+
"drawElementImageToTexture: react-native-webgpu is not installed");
|
|
280
|
+
}
|
|
281
|
+
if (!destination || !destination->info || !destination->info->texture) {
|
|
282
|
+
throw std::runtime_error(
|
|
283
|
+
"drawElementImageToTexture: `destination.texture` is required");
|
|
284
|
+
}
|
|
285
|
+
const auto &info = destination->info;
|
|
286
|
+
|
|
287
|
+
// Destination texture: only the 8-bit RGBA/BGRA formats the platform
|
|
288
|
+
// rasterizers produce natively are accepted (with a byte swizzle between
|
|
289
|
+
// the two); anything else would need a GPU blit.
|
|
290
|
+
wgpu::TexelCopyTextureInfo dst{};
|
|
291
|
+
Convertor conv;
|
|
292
|
+
if (!conv(dst.aspect, info->aspect) || !conv(dst.mipLevel, info->mipLevel) ||
|
|
293
|
+
!conv(dst.origin, info->origin) || !conv(dst.texture, info->texture)) {
|
|
294
|
+
throw std::runtime_error("drawElementImageToTexture: invalid destination");
|
|
295
|
+
}
|
|
296
|
+
const wgpu::Texture texture = info->texture->get();
|
|
297
|
+
bool destinationIsBgra = false;
|
|
298
|
+
switch (texture.GetFormat()) {
|
|
299
|
+
case wgpu::TextureFormat::RGBA8Unorm:
|
|
300
|
+
case wgpu::TextureFormat::RGBA8UnormSrgb:
|
|
301
|
+
destinationIsBgra = false;
|
|
302
|
+
break;
|
|
303
|
+
case wgpu::TextureFormat::BGRA8Unorm:
|
|
304
|
+
case wgpu::TextureFormat::BGRA8UnormSrgb:
|
|
305
|
+
destinationIsBgra = true;
|
|
306
|
+
break;
|
|
307
|
+
default:
|
|
308
|
+
throw std::runtime_error(
|
|
309
|
+
"drawElementImageToTexture: the destination texture must be "
|
|
310
|
+
"rgba8unorm, rgba8unorm-srgb, bgra8unorm or bgra8unorm-srgb");
|
|
311
|
+
}
|
|
312
|
+
if (!(texture.GetUsage() & wgpu::TextureUsage::CopyDst)) {
|
|
313
|
+
throw std::runtime_error(
|
|
314
|
+
"drawElementImageToTexture: the destination texture must have "
|
|
315
|
+
"GPUTextureUsage.COPY_DST");
|
|
316
|
+
}
|
|
317
|
+
if (texture.GetDimension() != wgpu::TextureDimension::e2D ||
|
|
318
|
+
texture.GetSampleCount() != 1) {
|
|
319
|
+
throw std::runtime_error(
|
|
320
|
+
"drawElementImageToTexture: the destination must be a single-sampled "
|
|
321
|
+
"2D texture");
|
|
322
|
+
}
|
|
323
|
+
if (dst.mipLevel >= texture.GetMipLevelCount()) {
|
|
324
|
+
throw std::runtime_error(
|
|
325
|
+
"drawElementImageToTexture: destination.mipLevel is out of range");
|
|
326
|
+
}
|
|
327
|
+
const uint32_t mipWidth =
|
|
328
|
+
std::max<uint32_t>(1, texture.GetWidth() >> dst.mipLevel);
|
|
329
|
+
const uint32_t mipHeight =
|
|
330
|
+
std::max<uint32_t>(1, texture.GetHeight() >> dst.mipLevel);
|
|
331
|
+
if (dst.origin.z >= texture.GetDepthOrArrayLayers()) {
|
|
332
|
+
throw std::runtime_error(
|
|
333
|
+
"drawElementImageToTexture: destination.origin.z is out of range");
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Source rectangle (points, view space). Omitted width/height mean "the
|
|
337
|
+
// whole view", which only the platform knows.
|
|
338
|
+
ViewSnapshotRequest request{};
|
|
339
|
+
request.viewTag = source->viewTag;
|
|
340
|
+
request.sourceX = source->sourceX.value_or(0.0);
|
|
341
|
+
request.sourceY = source->sourceY.value_or(0.0);
|
|
342
|
+
request.sourceWidth = source->sourceWidth.value_or(0.0);
|
|
343
|
+
request.sourceHeight = source->sourceHeight.value_or(0.0);
|
|
344
|
+
if (!std::isfinite(request.sourceX) || !std::isfinite(request.sourceY) ||
|
|
345
|
+
!std::isfinite(request.sourceWidth) ||
|
|
346
|
+
!std::isfinite(request.sourceHeight) ||
|
|
347
|
+
(source->sourceWidth.has_value() && request.sourceWidth <= 0) ||
|
|
348
|
+
(source->sourceHeight.has_value() && request.sourceHeight <= 0)) {
|
|
349
|
+
throw std::runtime_error(
|
|
350
|
+
"drawElementImageToTexture: the source rectangle must be finite with "
|
|
351
|
+
"a positive width and height");
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Destination size (pixels). Omitted means the source rectangle's natural
|
|
355
|
+
// pixel size (points times the device pixel ratio), which the platform
|
|
356
|
+
// computes; the bounds check then happens once the pixels are back.
|
|
357
|
+
if (destination->size.has_value()) {
|
|
358
|
+
wgpu::Extent3D size{};
|
|
359
|
+
if (!conv(size, destination->size.value())) {
|
|
360
|
+
throw std::runtime_error(
|
|
361
|
+
"drawElementImageToTexture: invalid destination.size");
|
|
362
|
+
}
|
|
363
|
+
if (size.width == 0 || size.height == 0 || size.depthOrArrayLayers != 1) {
|
|
364
|
+
throw std::runtime_error(
|
|
365
|
+
"drawElementImageToTexture: destination.size must have a positive "
|
|
366
|
+
"width and height and a depthOrArrayLayers of 1");
|
|
367
|
+
}
|
|
368
|
+
if (size.width > mipWidth - std::min(dst.origin.x, mipWidth) ||
|
|
369
|
+
size.height > mipHeight - std::min(dst.origin.y, mipHeight)) {
|
|
370
|
+
throw std::runtime_error(
|
|
371
|
+
"drawElementImageToTexture: destination.origin + destination.size "
|
|
372
|
+
"exceeds the texture's extent at destination.mipLevel");
|
|
373
|
+
}
|
|
374
|
+
request.width = size.width;
|
|
375
|
+
request.height = size.height;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// premultipliedAlpha defaults to false, like copyExternalImageToTexture.
|
|
379
|
+
const bool destinationPremultiplied =
|
|
380
|
+
info->premultipliedAlpha.value_or(false);
|
|
381
|
+
|
|
382
|
+
// Settle on the CALLING runtime's context (see GPUBuffer::mapAsync): the
|
|
383
|
+
// snapshot completes on the UI thread and is deposited into that context's
|
|
384
|
+
// mailbox; the texture write and the resolve then run on the runtime's own
|
|
385
|
+
// thread during its next tick.
|
|
386
|
+
auto context =
|
|
387
|
+
async::RuntimeContext::getOrCreate(runtime, _async->instance());
|
|
388
|
+
auto queue = _instance;
|
|
389
|
+
// Keeps the destination texture alive until the write is issued.
|
|
390
|
+
auto gpuTexture = info->texture;
|
|
391
|
+
return context->postTask(
|
|
392
|
+
[platform, request, dst, mipWidth, mipHeight, destinationIsBgra,
|
|
393
|
+
destinationPremultiplied, queue,
|
|
394
|
+
gpuTexture](const async::AsyncTaskHandle::ResolveFunction &resolve,
|
|
395
|
+
const async::AsyncTaskHandle::RejectFunction &reject) {
|
|
396
|
+
platform->snapshotView(
|
|
397
|
+
request,
|
|
398
|
+
[resolve, reject, dst, mipWidth, mipHeight, destinationIsBgra,
|
|
399
|
+
destinationPremultiplied, queue,
|
|
400
|
+
gpuTexture](ImageData image) mutable {
|
|
401
|
+
// Arbitrary (UI) thread: validate without touching JSI.
|
|
402
|
+
if (image.width == 0 || image.height == 0 ||
|
|
403
|
+
image.data.size() < image.width * image.height * 4) {
|
|
404
|
+
reject("drawElementImageToTexture: the view produced no "
|
|
405
|
+
"pixels (is it mounted with a non-zero size?)");
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
if (image.width >
|
|
409
|
+
mipWidth - std::min<uint32_t>(dst.origin.x, mipWidth) ||
|
|
410
|
+
image.height >
|
|
411
|
+
mipHeight - std::min<uint32_t>(dst.origin.y, mipHeight)) {
|
|
412
|
+
reject("drawElementImageToTexture: the view's natural pixel "
|
|
413
|
+
"size (" +
|
|
414
|
+
std::to_string(image.width) + "x" +
|
|
415
|
+
std::to_string(image.height) +
|
|
416
|
+
") does not fit in the destination texture at "
|
|
417
|
+
"destination.origin; pass destination.size to scale "
|
|
418
|
+
"it, or create a larger texture");
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
resolve([image = std::move(image), dst, destinationIsBgra,
|
|
422
|
+
destinationPremultiplied, queue,
|
|
423
|
+
gpuTexture](jsi::Runtime &) mutable -> jsi::Value {
|
|
424
|
+
// Owning runtime's thread. Bring the pixels into the
|
|
425
|
+
// destination's channel order and alpha representation, then
|
|
426
|
+
// issue the write.
|
|
427
|
+
const bool sourceIsBgra =
|
|
428
|
+
image.format == wgpu::TextureFormat::BGRA8Unorm ||
|
|
429
|
+
image.format == wgpu::TextureFormat::BGRA8UnormSrgb;
|
|
430
|
+
uint8_t *pixels = image.data.data();
|
|
431
|
+
const size_t byteLength = image.width * image.height * 4;
|
|
432
|
+
if (sourceIsBgra != destinationIsBgra) {
|
|
433
|
+
for (size_t i = 0; i + 3 < byteLength; i += 4) {
|
|
434
|
+
std::swap(pixels[i], pixels[i + 2]);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
convertAlpha(pixels, byteLength, image.premultiplied,
|
|
438
|
+
destinationPremultiplied);
|
|
439
|
+
wgpu::TexelCopyBufferLayout layout{};
|
|
440
|
+
layout.offset = 0;
|
|
441
|
+
layout.bytesPerRow = static_cast<uint32_t>(image.width * 4);
|
|
442
|
+
layout.rowsPerImage = static_cast<uint32_t>(image.height);
|
|
443
|
+
wgpu::Extent3D extent{static_cast<uint32_t>(image.width),
|
|
444
|
+
static_cast<uint32_t>(image.height), 1};
|
|
445
|
+
queue.WriteTexture(&dst, pixels, byteLength, &layout, &extent);
|
|
446
|
+
return jsi::Value::undefined();
|
|
447
|
+
});
|
|
448
|
+
},
|
|
449
|
+
[reject](std::string error) { reject(std::move(error)); });
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
|
|
196
453
|
void GPUQueue::writeTexture(std::shared_ptr<GPUImageCopyTexture> destination,
|
|
197
454
|
std::shared_ptr<ArrayBuffer> data,
|
|
198
455
|
std::shared_ptr<GPUImageDataLayout> dataLayout,
|
|
@@ -16,8 +16,11 @@
|
|
|
16
16
|
#include "ArrayBuffer.h"
|
|
17
17
|
#include "GPUBuffer.h"
|
|
18
18
|
#include "GPUCommandBuffer.h"
|
|
19
|
+
#include "GPUDrawElementImageDestination.h"
|
|
20
|
+
#include "GPUDrawElementImageSource.h"
|
|
19
21
|
#include "GPUImageCopyExternalImage.h"
|
|
20
22
|
#include "GPUImageCopyTextureTagged.h"
|
|
23
|
+
#include "VideoFrameBlit.h"
|
|
21
24
|
|
|
22
25
|
namespace rnwgpu {
|
|
23
26
|
|
|
@@ -27,10 +30,12 @@ class GPUQueue : public NativeObject<GPUQueue> {
|
|
|
27
30
|
public:
|
|
28
31
|
static constexpr const char *CLASS_NAME = "GPUQueue";
|
|
29
32
|
|
|
30
|
-
explicit GPUQueue(wgpu::Queue instance,
|
|
33
|
+
explicit GPUQueue(wgpu::Device device, wgpu::Queue instance,
|
|
31
34
|
std::shared_ptr<async::RuntimeContext> async,
|
|
35
|
+
std::shared_ptr<VideoFrameBlit> videoFrameBlit,
|
|
32
36
|
std::string label)
|
|
33
|
-
: NativeObject(CLASS_NAME),
|
|
37
|
+
: NativeObject(CLASS_NAME), _device(device), _instance(instance),
|
|
38
|
+
_async(async), _videoFrameBlit(std::move(videoFrameBlit)),
|
|
34
39
|
_label(label) {}
|
|
35
40
|
|
|
36
41
|
public:
|
|
@@ -46,11 +51,25 @@ public:
|
|
|
46
51
|
std::shared_ptr<ArrayBuffer> data,
|
|
47
52
|
std::shared_ptr<GPUImageDataLayout> dataLayout,
|
|
48
53
|
std::shared_ptr<GPUExtent3D> size);
|
|
54
|
+
// The source is an ImageBitmap (uploaded from the CPU) or, as a React
|
|
55
|
+
// Native extension, a NativeVideoFrame rendered into the destination through
|
|
56
|
+
// an external texture (see VideoFrameBlit).
|
|
49
57
|
void copyExternalImageToTexture(
|
|
50
58
|
std::shared_ptr<GPUImageCopyExternalImage> source,
|
|
51
59
|
std::shared_ptr<GPUImageCopyTextureTagged> destination,
|
|
52
60
|
std::shared_ptr<GPUExtent3D> copySize);
|
|
53
61
|
|
|
62
|
+
// Non-spec React Native extension modeled on the html-in-canvas proposal's
|
|
63
|
+
// GPUQueue.drawElementImageToTexture: rasterizes a native view (the
|
|
64
|
+
// "element") and writes the pixels into `destination.texture`. Unlike the
|
|
65
|
+
// web API it returns a Promise, because the view can only be rasterized on
|
|
66
|
+
// the platform UI thread; the texture write is issued on the calling
|
|
67
|
+
// runtime's thread right before the promise resolves, so work submitted
|
|
68
|
+
// after `await` observes the new contents.
|
|
69
|
+
async::AsyncTaskHandle drawElementImageToTexture(
|
|
70
|
+
jsi::Runtime &runtime, std::shared_ptr<GPUDrawElementImageSource> source,
|
|
71
|
+
std::shared_ptr<GPUDrawElementImageDestination> destination);
|
|
72
|
+
|
|
54
73
|
std::string getLabel() { return _label; }
|
|
55
74
|
void setLabel(const std::string &label) {
|
|
56
75
|
_label = label;
|
|
@@ -66,6 +85,8 @@ public:
|
|
|
66
85
|
installMethod(runtime, prototype, "writeTexture", &GPUQueue::writeTexture);
|
|
67
86
|
installMethod(runtime, prototype, "copyExternalImageToTexture",
|
|
68
87
|
&GPUQueue::copyExternalImageToTexture);
|
|
88
|
+
installMethodWithRuntime(runtime, prototype, "drawElementImageToTexture",
|
|
89
|
+
&GPUQueue::drawElementImageToTexture);
|
|
69
90
|
installGetterSetter(runtime, prototype, "label", &GPUQueue::getLabel,
|
|
70
91
|
&GPUQueue::setLabel);
|
|
71
92
|
}
|
|
@@ -73,8 +94,14 @@ public:
|
|
|
73
94
|
inline const wgpu::Queue get() { return _instance; }
|
|
74
95
|
|
|
75
96
|
private:
|
|
97
|
+
void copyVideoFrameToTexture(const GPUImageCopyExternalImage &source,
|
|
98
|
+
const GPUImageCopyTextureTagged &destination,
|
|
99
|
+
const wgpu::Extent3D &size);
|
|
100
|
+
|
|
101
|
+
wgpu::Device _device;
|
|
76
102
|
wgpu::Queue _instance;
|
|
77
103
|
std::shared_ptr<async::RuntimeContext> _async;
|
|
104
|
+
std::shared_ptr<VideoFrameBlit> _videoFrameBlit;
|
|
78
105
|
std::string _label;
|
|
79
106
|
};
|
|
80
107
|
|