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.
Files changed (61) hide show
  1. package/android/cpp/AndroidPlatformContext.h +100 -9
  2. package/android/cpp/AndroidVideoPlayer.h +307 -0
  3. package/android/cpp/cpp-adapter.cpp +66 -6
  4. package/android/src/main/java/com/webgpu/ViewSnapshot.java +162 -0
  5. package/android/src/main/java/com/webgpu/WebGPUModule.java +78 -0
  6. package/android/src/main/java/com/webgpu/WebGPUVideoPlayer.java +420 -0
  7. package/apple/ApplePlatformContext.h +14 -1
  8. package/apple/ApplePlatformContext.mm +176 -1
  9. package/apple/AppleVideoPlayer.mm +157 -47
  10. package/apple/WebGPUModule.mm +20 -1
  11. package/cpp/rnwgpu/PlatformContext.h +76 -1
  12. package/cpp/rnwgpu/RNWebGPUManager.cpp +7 -0
  13. package/cpp/rnwgpu/api/GPUDevice.cpp +2 -1
  14. package/cpp/rnwgpu/api/GPUDevice.h +6 -0
  15. package/cpp/rnwgpu/api/GPUQueue.cpp +261 -4
  16. package/cpp/rnwgpu/api/GPUQueue.h +29 -2
  17. package/cpp/rnwgpu/api/VideoFrameBlit.h +355 -0
  18. package/cpp/rnwgpu/api/VideoPlayer.h +50 -3
  19. package/cpp/rnwgpu/api/descriptors/GPUDrawElementImageDestination.h +60 -0
  20. package/cpp/rnwgpu/api/descriptors/GPUDrawElementImageSource.h +106 -0
  21. package/cpp/rnwgpu/api/descriptors/GPUImageCopyExternalImage.h +30 -3
  22. package/lib/commonjs/importDevice.js +2 -2
  23. package/lib/commonjs/index.js.map +1 -1
  24. package/lib/commonjs/main/index.js +45 -0
  25. package/lib/commonjs/main/index.js.map +1 -1
  26. package/lib/commonjs/main/index.web.js +11 -0
  27. package/lib/commonjs/main/index.web.js.map +1 -1
  28. package/lib/commonjs/video.js +58 -0
  29. package/lib/commonjs/video.js.map +1 -0
  30. package/lib/module/importDevice.js +2 -2
  31. package/lib/module/index.js.map +1 -1
  32. package/lib/module/main/index.js +34 -0
  33. package/lib/module/main/index.js.map +1 -1
  34. package/lib/module/main/index.web.js +1 -0
  35. package/lib/module/main/index.web.js.map +1 -1
  36. package/lib/module/video.js +49 -0
  37. package/lib/module/video.js.map +1 -0
  38. package/lib/typescript/lib/commonjs/video.d.ts +30 -0
  39. package/lib/typescript/lib/commonjs/video.d.ts.map +1 -0
  40. package/lib/typescript/lib/module/main/index.d.ts +1 -0
  41. package/lib/typescript/lib/module/main/index.web.d.ts +1 -0
  42. package/lib/typescript/lib/module/video.d.ts +4 -0
  43. package/lib/typescript/lib/module/video.d.ts.map +1 -0
  44. package/lib/typescript/src/importDevice.d.ts +2 -2
  45. package/lib/typescript/src/index.d.ts +8 -3
  46. package/lib/typescript/src/index.d.ts.map +1 -1
  47. package/lib/typescript/src/main/index.d.ts +1 -0
  48. package/lib/typescript/src/main/index.d.ts.map +1 -1
  49. package/lib/typescript/src/main/index.web.d.ts +1 -0
  50. package/lib/typescript/src/main/index.web.d.ts.map +1 -1
  51. package/lib/typescript/src/types.d.ts +32 -0
  52. package/lib/typescript/src/types.d.ts.map +1 -1
  53. package/lib/typescript/src/video.d.ts +41 -0
  54. package/lib/typescript/src/video.d.ts.map +1 -0
  55. package/package.json +1 -1
  56. package/src/importDevice.ts +2 -2
  57. package/src/index.tsx +57 -3
  58. package/src/main/index.tsx +50 -0
  59. package/src/main/index.web.tsx +1 -0
  60. package/src/types.ts +89 -7
  61. package/src/video.ts +69 -0
@@ -0,0 +1,355 @@
1
+ #pragma once
2
+
3
+ #include <array>
4
+ #include <cstring>
5
+ #include <memory>
6
+ #include <mutex>
7
+ #include <stdexcept>
8
+ #include <string>
9
+ #include <unordered_map>
10
+ #include <utility>
11
+ #include <vector>
12
+
13
+ #include "webgpu/webgpu_cpp.h"
14
+
15
+ #include "GPUExternalTexture.h"
16
+ #include "GPUExternalTextureDescriptor.h"
17
+ #include "VideoFrame.h"
18
+
19
+ namespace rnwgpu {
20
+
21
+ // Renders a native video frame into a region of a regular texture. The frame
22
+ // is imported as an external texture, so Dawn does the YUV to RGB conversion
23
+ // and applies the rotation and the mirroring; a full-screen triangle then
24
+ // samples it into the destination. This is what backs
25
+ // GPUQueue.copyExternalImageToTexture for NativeVideoFrame sources.
26
+ //
27
+ // One instance per device, shared by its GPUQueue wrappers (a new wrapper is
28
+ // created on every `device.queue` access): it caches the shader, the layouts
29
+ // and one pipeline per destination format.
30
+ class VideoFrameBlit {
31
+ public:
32
+ struct Request {
33
+ std::shared_ptr<VideoFrame> frame;
34
+ // The region of the upright frame (after rotation and mirroring) to copy.
35
+ uint32_t originX = 0;
36
+ uint32_t originY = 0;
37
+ uint32_t width = 0;
38
+ uint32_t height = 0;
39
+ bool flipY = false;
40
+ double rotation = 0;
41
+ bool mirrored = false;
42
+ // The destination region.
43
+ wgpu::Texture texture;
44
+ uint32_t mipLevel = 0;
45
+ uint32_t dstX = 0;
46
+ uint32_t dstY = 0;
47
+ };
48
+
49
+ void copy(const wgpu::Device &device, const wgpu::Queue &queue,
50
+ const Request &request) {
51
+ if (!request.frame || request.frame->handle().handle == nullptr) {
52
+ throw std::runtime_error(
53
+ "copyExternalImageToTexture: the NativeVideoFrame was released");
54
+ }
55
+ if (!request.texture) {
56
+ throw std::runtime_error(
57
+ "copyExternalImageToTexture: destination.texture is required");
58
+ }
59
+ if (!(request.texture.GetUsage() & wgpu::TextureUsage::RenderAttachment)) {
60
+ throw std::runtime_error(
61
+ "copyExternalImageToTexture: a native frame is rendered into the "
62
+ "destination, which needs GPUTextureUsage.RENDER_ATTACHMENT");
63
+ }
64
+ const wgpu::TextureFormat format = request.texture.GetFormat();
65
+ if (!isSupportedDestination(format)) {
66
+ throw std::runtime_error(
67
+ "copyExternalImageToTexture: a native frame can only be copied "
68
+ "into an rgba8unorm, bgra8unorm (or -srgb), rgba16float or "
69
+ "rgb10a2unorm texture");
70
+ }
71
+ if (request.mipLevel >= request.texture.GetMipLevelCount()) {
72
+ throw std::runtime_error(
73
+ "copyExternalImageToTexture: destination.mipLevel is out of range");
74
+ }
75
+ const uint32_t mipWidth =
76
+ std::max(1u, request.texture.GetWidth() >> request.mipLevel);
77
+ const uint32_t mipHeight =
78
+ std::max(1u, request.texture.GetHeight() >> request.mipLevel);
79
+ if (request.width == 0 || request.height == 0) {
80
+ return;
81
+ }
82
+ if (request.dstX > mipWidth || request.width > mipWidth - request.dstX ||
83
+ request.dstY > mipHeight || request.height > mipHeight - request.dstY) {
84
+ throw std::runtime_error(
85
+ "copyExternalImageToTexture: the destination region is outside "
86
+ "the texture");
87
+ }
88
+
89
+ // The frame as the shader sees it: Dawn swaps the apparent size of a
90
+ // frame rotated by 90 or 270 degrees.
91
+ const auto &handle = request.frame->handle();
92
+ const int quadrant = static_cast<int>(request.rotation / 90.0) & 3;
93
+ const bool swapped = quadrant == 1 || quadrant == 3;
94
+ const uint32_t frameWidth = swapped ? handle.height : handle.width;
95
+ const uint32_t frameHeight = swapped ? handle.width : handle.height;
96
+ if (request.originX > frameWidth ||
97
+ request.width > frameWidth - request.originX ||
98
+ request.originY > frameHeight ||
99
+ request.height > frameHeight - request.originY) {
100
+ throw std::runtime_error(
101
+ "copyExternalImageToTexture: the source region is outside the "
102
+ "frame");
103
+ }
104
+
105
+ // Importing the frame begins the access to its surface; destroy() ends
106
+ // it once the commands that sample it are submitted.
107
+ auto descriptor = std::make_shared<GPUExternalTextureDescriptor>();
108
+ descriptor->source = request.frame;
109
+ descriptor->label = "copyExternalImageToTexture";
110
+ descriptor->rotation = request.rotation;
111
+ descriptor->mirrored = request.mirrored;
112
+ auto external = GPUExternalTexture::Create(device, descriptor);
113
+
114
+ const Resources &resources = resourcesFor(device, format);
115
+
116
+ // uv = position01 * uvScale + uvOffset, with position01 running from the
117
+ // top-left corner of the copy region; flipY reads the rows bottom-up.
118
+ const float scaleX =
119
+ static_cast<float>(request.width) / static_cast<float>(frameWidth);
120
+ const float scaleY =
121
+ static_cast<float>(request.height) / static_cast<float>(frameHeight);
122
+ const float offsetX =
123
+ static_cast<float>(request.originX) / static_cast<float>(frameWidth);
124
+ const float offsetY =
125
+ static_cast<float>(request.originY) / static_cast<float>(frameHeight);
126
+ std::array<float, 20> uniforms = {};
127
+ const std::vector<double> matrix = external->getYuvToRgbMatrix();
128
+ for (size_t i = 0; i < 12 && i < matrix.size(); i++) {
129
+ uniforms[i] = static_cast<float>(matrix[i]);
130
+ }
131
+ uniforms[12] = scaleX;
132
+ uniforms[13] = request.flipY ? -scaleY : scaleY;
133
+ uniforms[14] = offsetX;
134
+ uniforms[15] = request.flipY ? offsetY + scaleY : offsetY;
135
+ const uint32_t decodeSrgb = isSrgb(format) ? 1u : 0u;
136
+ std::memcpy(&uniforms[16], &decodeSrgb, sizeof(decodeSrgb));
137
+
138
+ wgpu::BufferDescriptor bufferDesc = {};
139
+ bufferDesc.label = "copyExternalImageToTexture uniforms";
140
+ bufferDesc.size = sizeof(uniforms);
141
+ bufferDesc.usage = wgpu::BufferUsage::Uniform;
142
+ bufferDesc.mappedAtCreation = true;
143
+ wgpu::Buffer uniformBuffer = device.CreateBuffer(&bufferDesc);
144
+ std::memcpy(uniformBuffer.GetMappedRange(), uniforms.data(),
145
+ sizeof(uniforms));
146
+ uniformBuffer.Unmap();
147
+
148
+ wgpu::ExternalTextureBindingEntry externalEntry = {};
149
+ externalEntry.externalTexture = external->get();
150
+ std::array<wgpu::BindGroupEntry, 3> entries = {};
151
+ entries[0].binding = 0;
152
+ entries[0].nextInChain = &externalEntry;
153
+ entries[1].binding = 1;
154
+ entries[1].sampler = resources.sampler;
155
+ entries[2].binding = 2;
156
+ entries[2].buffer = uniformBuffer;
157
+ entries[2].size = sizeof(uniforms);
158
+ wgpu::BindGroupDescriptor bindGroupDesc = {};
159
+ bindGroupDesc.layout = resources.bindGroupLayout;
160
+ bindGroupDesc.entryCount = entries.size();
161
+ bindGroupDesc.entries = entries.data();
162
+ wgpu::BindGroup bindGroup = device.CreateBindGroup(&bindGroupDesc);
163
+
164
+ wgpu::TextureViewDescriptor viewDesc = {};
165
+ viewDesc.baseMipLevel = request.mipLevel;
166
+ viewDesc.mipLevelCount = 1;
167
+ viewDesc.baseArrayLayer = 0;
168
+ viewDesc.arrayLayerCount = 1;
169
+ viewDesc.dimension = wgpu::TextureViewDimension::e2D;
170
+ wgpu::TextureView target = request.texture.CreateView(&viewDesc);
171
+
172
+ // The rest of the texture is left untouched: the pass loads the existing
173
+ // contents and the viewport and scissor confine the draw to the region.
174
+ wgpu::RenderPassColorAttachment attachment = {};
175
+ attachment.view = target;
176
+ attachment.loadOp = wgpu::LoadOp::Load;
177
+ attachment.storeOp = wgpu::StoreOp::Store;
178
+ wgpu::RenderPassDescriptor passDesc = {};
179
+ passDesc.label = "copyExternalImageToTexture";
180
+ passDesc.colorAttachmentCount = 1;
181
+ passDesc.colorAttachments = &attachment;
182
+
183
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
184
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&passDesc);
185
+ pass.SetPipeline(resources.pipeline);
186
+ pass.SetBindGroup(0, bindGroup);
187
+ pass.SetViewport(static_cast<float>(request.dstX),
188
+ static_cast<float>(request.dstY),
189
+ static_cast<float>(request.width),
190
+ static_cast<float>(request.height), 0.0f, 1.0f);
191
+ pass.SetScissorRect(request.dstX, request.dstY, request.width,
192
+ request.height);
193
+ pass.Draw(3);
194
+ pass.End();
195
+ wgpu::CommandBuffer commands = encoder.Finish();
196
+ queue.Submit(1, &commands);
197
+ external->destroy();
198
+ }
199
+
200
+ private:
201
+ struct Resources {
202
+ wgpu::BindGroupLayout bindGroupLayout;
203
+ wgpu::Sampler sampler;
204
+ wgpu::RenderPipeline pipeline;
205
+ };
206
+
207
+ static bool isSrgb(wgpu::TextureFormat format) {
208
+ return format == wgpu::TextureFormat::RGBA8UnormSrgb ||
209
+ format == wgpu::TextureFormat::BGRA8UnormSrgb;
210
+ }
211
+
212
+ static bool isSupportedDestination(wgpu::TextureFormat format) {
213
+ switch (format) {
214
+ case wgpu::TextureFormat::RGBA8Unorm:
215
+ case wgpu::TextureFormat::RGBA8UnormSrgb:
216
+ case wgpu::TextureFormat::BGRA8Unorm:
217
+ case wgpu::TextureFormat::BGRA8UnormSrgb:
218
+ case wgpu::TextureFormat::RGBA16Float:
219
+ case wgpu::TextureFormat::RGB10A2Unorm:
220
+ return true;
221
+ default:
222
+ return false;
223
+ }
224
+ }
225
+
226
+ static constexpr const char *kShader = R"(
227
+ struct Uniforms {
228
+ // The rows of the 3x4 matrix mapping the sampled texel to R'G'B'
229
+ // (GPUExternalTexture.yuvToRgbMatrix; the identity outside Android YUV).
230
+ yuvToRgb0: vec4f,
231
+ yuvToRgb1: vec4f,
232
+ yuvToRgb2: vec4f,
233
+ // uv = position01 * uvScale + uvOffset
234
+ uvScale: vec2f,
235
+ uvOffset: vec2f,
236
+ // x: decode sRGB to linear, for an sRGB destination that re-encodes on write
237
+ flags: vec4u,
238
+ };
239
+
240
+ @group(0) @binding(0) var frame: texture_external;
241
+ @group(0) @binding(1) var frameSampler: sampler;
242
+ @group(0) @binding(2) var<uniform> u: Uniforms;
243
+
244
+ struct VsOut {
245
+ @builtin(position) position: vec4f,
246
+ @location(0) uv: vec2f,
247
+ };
248
+
249
+ @vertex fn vs(@builtin(vertex_index) index: u32) -> VsOut {
250
+ // A triangle covering the viewport; position01 runs from its top-left
251
+ // corner, like image coordinates.
252
+ var corners = array<vec2f, 3>(vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0));
253
+ let corner = corners[index];
254
+ var out: VsOut;
255
+ out.position = vec4f(corner, 0.0, 1.0);
256
+ let position01 = vec2f(corner.x * 0.5 + 0.5, 0.5 - corner.y * 0.5);
257
+ out.uv = position01 * u.uvScale + u.uvOffset;
258
+ return out;
259
+ }
260
+
261
+ fn srgbToLinear(c: vec3f) -> vec3f {
262
+ let low = c / 12.92;
263
+ let high = pow((c + 0.055) / 1.055, vec3f(2.4));
264
+ return select(high, low, c <= vec3f(0.04045));
265
+ }
266
+
267
+ @fragment fn fs(in: VsOut) -> @location(0) vec4f {
268
+ let sampled = textureSampleBaseClampToEdge(frame, frameSampler, in.uv);
269
+ let texel = vec4f(sampled.rgb, 1.0);
270
+ var rgb = vec3f(dot(u.yuvToRgb0, texel), dot(u.yuvToRgb1, texel), dot(u.yuvToRgb2, texel));
271
+ rgb = clamp(rgb, vec3f(0.0), vec3f(1.0));
272
+ if (u.flags.x == 1u) {
273
+ rgb = srgbToLinear(rgb);
274
+ }
275
+ return vec4f(rgb, 1.0);
276
+ }
277
+ )";
278
+
279
+ const Resources &resourcesFor(const wgpu::Device &device,
280
+ wgpu::TextureFormat format) {
281
+ std::lock_guard<std::mutex> lock(_mutex);
282
+ auto found = _resources.find(static_cast<WGPUTextureFormat>(format));
283
+ if (found != _resources.end()) {
284
+ return found->second;
285
+ }
286
+ if (!_module) {
287
+ wgpu::ShaderSourceWGSL wgsl = {};
288
+ wgsl.code = kShader;
289
+ wgpu::ShaderModuleDescriptor moduleDesc = {};
290
+ moduleDesc.label = "copyExternalImageToTexture";
291
+ moduleDesc.nextInChain = &wgsl;
292
+ _module = device.CreateShaderModule(&moduleDesc);
293
+
294
+ wgpu::ExternalTextureBindingLayout externalLayout = {};
295
+ std::array<wgpu::BindGroupLayoutEntry, 3> entries = {};
296
+ entries[0].binding = 0;
297
+ entries[0].visibility = wgpu::ShaderStage::Fragment;
298
+ entries[0].nextInChain = &externalLayout;
299
+ entries[1].binding = 1;
300
+ entries[1].visibility = wgpu::ShaderStage::Fragment;
301
+ entries[1].sampler.type = wgpu::SamplerBindingType::Filtering;
302
+ entries[2].binding = 2;
303
+ entries[2].visibility =
304
+ wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment;
305
+ entries[2].buffer.type = wgpu::BufferBindingType::Uniform;
306
+ wgpu::BindGroupLayoutDescriptor layoutDesc = {};
307
+ layoutDesc.label = "copyExternalImageToTexture";
308
+ layoutDesc.entryCount = entries.size();
309
+ layoutDesc.entries = entries.data();
310
+ _bindGroupLayout = device.CreateBindGroupLayout(&layoutDesc);
311
+
312
+ wgpu::PipelineLayoutDescriptor pipelineLayoutDesc = {};
313
+ pipelineLayoutDesc.bindGroupLayoutCount = 1;
314
+ pipelineLayoutDesc.bindGroupLayouts = &_bindGroupLayout;
315
+ _pipelineLayout = device.CreatePipelineLayout(&pipelineLayoutDesc);
316
+
317
+ wgpu::SamplerDescriptor samplerDesc = {};
318
+ samplerDesc.magFilter = wgpu::FilterMode::Linear;
319
+ samplerDesc.minFilter = wgpu::FilterMode::Linear;
320
+ _sampler = device.CreateSampler(&samplerDesc);
321
+ }
322
+
323
+ wgpu::ColorTargetState target = {};
324
+ target.format = format;
325
+ wgpu::FragmentState fragment = {};
326
+ fragment.module = _module;
327
+ fragment.entryPoint = "fs";
328
+ fragment.targetCount = 1;
329
+ fragment.targets = &target;
330
+ wgpu::RenderPipelineDescriptor pipelineDesc = {};
331
+ pipelineDesc.label = "copyExternalImageToTexture";
332
+ pipelineDesc.layout = _pipelineLayout;
333
+ pipelineDesc.vertex.module = _module;
334
+ pipelineDesc.vertex.entryPoint = "vs";
335
+ pipelineDesc.fragment = &fragment;
336
+ pipelineDesc.primitive.topology = wgpu::PrimitiveTopology::TriangleList;
337
+
338
+ Resources resources;
339
+ resources.bindGroupLayout = _bindGroupLayout;
340
+ resources.sampler = _sampler;
341
+ resources.pipeline = device.CreateRenderPipeline(&pipelineDesc);
342
+ auto inserted = _resources.emplace(static_cast<WGPUTextureFormat>(format),
343
+ std::move(resources));
344
+ return inserted.first->second;
345
+ }
346
+
347
+ std::mutex _mutex;
348
+ wgpu::ShaderModule _module;
349
+ wgpu::BindGroupLayout _bindGroupLayout;
350
+ wgpu::PipelineLayout _pipelineLayout;
351
+ wgpu::Sampler _sampler;
352
+ std::unordered_map<WGPUTextureFormat, Resources> _resources;
353
+ };
354
+
355
+ } // namespace rnwgpu
@@ -14,7 +14,9 @@ namespace rnwgpu {
14
14
  namespace jsi = facebook::jsi;
15
15
 
16
16
  // JSI wrapper around a platform-specific IVideoPlayer. Hands out fresh
17
- // VideoFrame handles each time the underlying decoder produces a new frame.
17
+ // VideoFrame handles each time the underlying decoder produces a new frame,
18
+ // and exposes playback controls modeled on HTMLMediaElement (see
19
+ // VideoPlayer in src/types.ts).
18
20
  class VideoPlayer : public NativeObject<VideoPlayer> {
19
21
  public:
20
22
  static constexpr const char *CLASS_NAME = "VideoPlayer";
@@ -27,8 +29,7 @@ public:
27
29
  // Returns the latest decoded frame, or null if no new frame is ready yet.
28
30
  // Callers should poll this from their render loop and skip rendering (or
29
31
  // reuse the last frame's texture) when null.
30
- std::variant<std::nullptr_t, std::shared_ptr<VideoFrame>>
31
- copyLatestFrame() {
32
+ std::variant<std::nullptr_t, std::shared_ptr<VideoFrame>> copyLatestFrame() {
32
33
  if (!_impl) {
33
34
  return nullptr;
34
35
  }
@@ -51,6 +52,37 @@ public:
51
52
  }
52
53
  }
53
54
 
55
+ // A released player reads as paused, at the start, with no metadata.
56
+ bool getPaused() { return _impl ? _impl->paused() : true; }
57
+
58
+ double getCurrentTime() { return _impl ? _impl->currentTime() : 0; }
59
+ void setCurrentTime(double seconds) {
60
+ if (_impl) {
61
+ _impl->seek(seconds);
62
+ }
63
+ }
64
+
65
+ double getDuration() { return _impl ? _impl->duration() : 0; }
66
+
67
+ double getVolume() { return _impl ? _impl->volume() : 0; }
68
+ void setVolume(double volume) {
69
+ if (_impl) {
70
+ _impl->setVolume(volume);
71
+ }
72
+ }
73
+
74
+ bool getLoop() { return _impl ? _impl->loop() : false; }
75
+ void setLoop(bool loop) {
76
+ if (_impl) {
77
+ _impl->setLoop(loop);
78
+ }
79
+ }
80
+
81
+ double getVideoWidth() { return _impl ? _impl->videoWidth() : 0; }
82
+ double getVideoHeight() { return _impl ? _impl->videoHeight() : 0; }
83
+ double getRotation() { return _impl ? _impl->rotation() : 0; }
84
+ double getFrameRate() { return _impl ? _impl->frameRate() : 0; }
85
+
54
86
  void release() { _impl.reset(); }
55
87
 
56
88
  static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) {
@@ -60,6 +92,21 @@ public:
60
92
  installMethod(runtime, prototype, "play", &VideoPlayer::play);
61
93
  installMethod(runtime, prototype, "pause", &VideoPlayer::pause);
62
94
  installMethod(runtime, prototype, "release", &VideoPlayer::release);
95
+ installGetter(runtime, prototype, "paused", &VideoPlayer::getPaused);
96
+ installGetterSetter(runtime, prototype, "currentTime",
97
+ &VideoPlayer::getCurrentTime,
98
+ &VideoPlayer::setCurrentTime);
99
+ installGetter(runtime, prototype, "duration", &VideoPlayer::getDuration);
100
+ installGetterSetter(runtime, prototype, "volume", &VideoPlayer::getVolume,
101
+ &VideoPlayer::setVolume);
102
+ installGetterSetter(runtime, prototype, "loop", &VideoPlayer::getLoop,
103
+ &VideoPlayer::setLoop);
104
+ installGetter(runtime, prototype, "videoWidth",
105
+ &VideoPlayer::getVideoWidth);
106
+ installGetter(runtime, prototype, "videoHeight",
107
+ &VideoPlayer::getVideoHeight);
108
+ installGetter(runtime, prototype, "rotation", &VideoPlayer::getRotation);
109
+ installGetter(runtime, prototype, "frameRate", &VideoPlayer::getFrameRate);
63
110
  }
64
111
 
65
112
  private:
@@ -0,0 +1,60 @@
1
+ #pragma once
2
+
3
+ #include <memory>
4
+ #include <optional>
5
+
6
+ #include "JSIConverter.h"
7
+
8
+ #include "GPUExtent3D.h"
9
+ #include "GPUImageCopyTextureTagged.h"
10
+
11
+ namespace jsi = facebook::jsi;
12
+
13
+ namespace rnwgpu {
14
+
15
+ // The `destination` argument of GPUQueue.drawElementImageToTexture:
16
+ // dictionary GPUDrawElementImageDestination : GPUCopyExternalImageDestInfo {
17
+ // GPUExtent3D size;
18
+ // };
19
+ // `size` is the pixel size of the region the view is drawn into. When omitted
20
+ // it is the source rectangle's natural pixel size (points times the device
21
+ // pixel ratio).
22
+ struct GPUDrawElementImageDestination {
23
+ std::shared_ptr<GPUImageCopyTextureTagged> info;
24
+ std::optional<std::shared_ptr<GPUExtent3D>> size;
25
+ };
26
+
27
+ } // namespace rnwgpu
28
+
29
+ namespace rnwgpu {
30
+
31
+ template <>
32
+ struct JSIConverter<std::shared_ptr<rnwgpu::GPUDrawElementImageDestination>> {
33
+ static std::shared_ptr<rnwgpu::GPUDrawElementImageDestination>
34
+ fromJSI(jsi::Runtime &runtime, const jsi::Value &arg, bool outOfBounds) {
35
+ auto result = std::make_unique<rnwgpu::GPUDrawElementImageDestination>();
36
+ if (outOfBounds || !arg.isObject()) {
37
+ throw std::runtime_error(
38
+ "drawElementImageToTexture: expected a destination dictionary");
39
+ }
40
+ result->info =
41
+ JSIConverter<std::shared_ptr<GPUImageCopyTextureTagged>>::fromJSI(
42
+ runtime, arg, false);
43
+ auto obj = arg.getObject(runtime);
44
+ if (obj.hasProperty(runtime, "size")) {
45
+ result->size =
46
+ JSIConverter<std::optional<std::shared_ptr<GPUExtent3D>>>::fromJSI(
47
+ runtime, obj.getProperty(runtime, "size"), false);
48
+ }
49
+ return result;
50
+ }
51
+
52
+ static jsi::Value
53
+ toJSI(jsi::Runtime &runtime,
54
+ std::shared_ptr<rnwgpu::GPUDrawElementImageDestination> arg) {
55
+ throw std::runtime_error(
56
+ "Invalid GPUDrawElementImageDestination::toJSI()");
57
+ }
58
+ };
59
+
60
+ } // namespace rnwgpu
@@ -0,0 +1,106 @@
1
+ #pragma once
2
+
3
+ #include <memory>
4
+ #include <optional>
5
+ #include <string>
6
+
7
+ #include "JSIConverter.h"
8
+
9
+ namespace jsi = facebook::jsi;
10
+
11
+ namespace rnwgpu {
12
+
13
+ // The `source` argument of GPUQueue.drawElementImageToTexture, modeled on the
14
+ // html-in-canvas proposal's GPUDrawElementImageSource. On the web `source` is
15
+ // an Element; here it is a native React Native view, identified by its React
16
+ // tag. The optional rectangle selects the part of the view to draw, in the
17
+ // view's own coordinate space and in density-independent points (React Native
18
+ // layout units); it defaults to the whole view.
19
+ struct GPUDrawElementImageSource {
20
+ int viewTag = 0; // resolved from `source`
21
+ std::optional<double> sourceX; // float
22
+ std::optional<double> sourceY; // float
23
+ std::optional<double> sourceWidth; // float
24
+ std::optional<double> sourceHeight; // float
25
+ };
26
+
27
+ } // namespace rnwgpu
28
+
29
+ namespace rnwgpu {
30
+
31
+ template <>
32
+ struct JSIConverter<std::shared_ptr<rnwgpu::GPUDrawElementImageSource>> {
33
+ // Resolve `source` to a React tag. Accepted shapes:
34
+ // - a number: the tag itself (what findNodeHandle returns)
35
+ // - a React Native host component instance: on the New Architecture these
36
+ // carry the tag as `__nativeTag` (ReactFabricHostComponent /
37
+ // ReactNativeElement), which is also what findNodeHandle reads
38
+ // - a ref object (`{ current }`) wrapping either of the above
39
+ // The JS wrapper on the main runtime additionally runs findNodeHandle so
40
+ // class components and legacy instances resolve too; this native fallback is
41
+ // what worklet runtimes (no findNodeHandle) get.
42
+ static int resolveViewTag(jsi::Runtime &runtime, const jsi::Value &value,
43
+ int depth = 0) {
44
+ if (value.isNumber()) {
45
+ return static_cast<int>(value.getNumber());
46
+ }
47
+ if (value.isObject() && depth < 2) {
48
+ auto obj = value.getObject(runtime);
49
+ if (obj.hasProperty(runtime, "__nativeTag")) {
50
+ auto tag = obj.getProperty(runtime, "__nativeTag");
51
+ if (tag.isNumber()) {
52
+ return static_cast<int>(tag.getNumber());
53
+ }
54
+ }
55
+ if (obj.hasProperty(runtime, "current")) {
56
+ return resolveViewTag(runtime, obj.getProperty(runtime, "current"),
57
+ depth + 1);
58
+ }
59
+ }
60
+ throw std::runtime_error(
61
+ "drawElementImageToTexture: `source` must be a React Native view "
62
+ "(a host component ref, its instance, or the tag returned by "
63
+ "findNodeHandle)");
64
+ }
65
+
66
+ static std::shared_ptr<rnwgpu::GPUDrawElementImageSource>
67
+ fromJSI(jsi::Runtime &runtime, const jsi::Value &arg, bool outOfBounds) {
68
+ auto result = std::make_unique<rnwgpu::GPUDrawElementImageSource>();
69
+ if (outOfBounds || !arg.isObject()) {
70
+ throw std::runtime_error(
71
+ "drawElementImageToTexture: expected a source dictionary");
72
+ }
73
+ auto obj = arg.getObject(runtime);
74
+ if (!obj.hasProperty(runtime, "source")) {
75
+ throw std::runtime_error(
76
+ "drawElementImageToTexture: `source.source` is required");
77
+ }
78
+ result->viewTag =
79
+ resolveViewTag(runtime, obj.getProperty(runtime, "source"));
80
+ if (obj.hasProperty(runtime, "sourceX")) {
81
+ result->sourceX = JSIConverter<std::optional<double>>::fromJSI(
82
+ runtime, obj.getProperty(runtime, "sourceX"), false);
83
+ }
84
+ if (obj.hasProperty(runtime, "sourceY")) {
85
+ result->sourceY = JSIConverter<std::optional<double>>::fromJSI(
86
+ runtime, obj.getProperty(runtime, "sourceY"), false);
87
+ }
88
+ if (obj.hasProperty(runtime, "sourceWidth")) {
89
+ result->sourceWidth = JSIConverter<std::optional<double>>::fromJSI(
90
+ runtime, obj.getProperty(runtime, "sourceWidth"), false);
91
+ }
92
+ if (obj.hasProperty(runtime, "sourceHeight")) {
93
+ result->sourceHeight = JSIConverter<std::optional<double>>::fromJSI(
94
+ runtime, obj.getProperty(runtime, "sourceHeight"), false);
95
+ }
96
+ return result;
97
+ }
98
+
99
+ static jsi::Value
100
+ toJSI(jsi::Runtime &runtime,
101
+ std::shared_ptr<rnwgpu::GPUDrawElementImageSource> arg) {
102
+ throw std::runtime_error("Invalid GPUDrawElementImageSource::toJSI()");
103
+ }
104
+ };
105
+
106
+ } // namespace rnwgpu
@@ -12,15 +12,24 @@
12
12
 
13
13
  #include "GPUOrigin2D.h"
14
14
  #include "ImageBitmap.h"
15
+ #include "VideoFrame.h"
15
16
 
16
17
  namespace jsi = facebook::jsi;
17
18
 
18
19
  namespace rnwgpu {
19
20
 
21
+ // GPUCopyExternalImageSourceInfo. The source is either an ImageBitmap (the
22
+ // spec's CPU upload) or, as a React Native extension, a NativeVideoFrame that
23
+ // is rendered into the destination through an external texture (see
24
+ // VideoFrameBlit). `rotation` and `mirrored` only apply to a native frame and
25
+ // mirror the extension of GPUExternalTextureDescriptor.
20
26
  struct GPUImageCopyExternalImage {
21
- std::shared_ptr<ImageBitmap> source; // GPUImageCopyExternalImageSource
27
+ std::shared_ptr<ImageBitmap> source;
28
+ std::shared_ptr<VideoFrame> videoFrame;
22
29
  std::optional<std::shared_ptr<GPUOrigin2D>> origin; // GPUOrigin2DStrict
23
30
  std::optional<bool> flipY; // boolean
31
+ std::optional<double> rotation;
32
+ std::optional<bool> mirrored;
24
33
  };
25
34
 
26
35
  } // namespace rnwgpu
@@ -36,8 +45,14 @@ struct JSIConverter<std::shared_ptr<rnwgpu::GPUImageCopyExternalImage>> {
36
45
  auto obj = arg.getObject(runtime);
37
46
  if (obj.hasProperty(runtime, "source")) {
38
47
  auto prop = obj.getProperty(runtime, "source");
39
- result->source = JSIConverter<std::shared_ptr<ImageBitmap>>::fromJSI(
40
- runtime, prop, false);
48
+ if (prop.isObject() &&
49
+ prop.getObject(runtime).hasNativeState<VideoFrame>(runtime)) {
50
+ result->videoFrame =
51
+ prop.getObject(runtime).getNativeState<VideoFrame>(runtime);
52
+ } else {
53
+ result->source = JSIConverter<std::shared_ptr<ImageBitmap>>::fromJSI(
54
+ runtime, prop, false);
55
+ }
41
56
  }
42
57
  if (obj.hasProperty(runtime, "origin")) {
43
58
  auto prop = obj.getProperty(runtime, "origin");
@@ -49,6 +64,18 @@ struct JSIConverter<std::shared_ptr<rnwgpu::GPUImageCopyExternalImage>> {
49
64
  auto prop = obj.getProperty(runtime, "flipY");
50
65
  result->flipY = JSIConverter<bool>::fromJSI(runtime, prop, false);
51
66
  }
67
+ if (obj.hasProperty(runtime, "rotation")) {
68
+ auto prop = obj.getProperty(runtime, "rotation");
69
+ if (prop.isNumber()) {
70
+ result->rotation = prop.asNumber();
71
+ }
72
+ }
73
+ if (obj.hasProperty(runtime, "mirrored")) {
74
+ auto prop = obj.getProperty(runtime, "mirrored");
75
+ if (prop.isBool()) {
76
+ result->mirrored = prop.getBool();
77
+ }
78
+ }
52
79
  }
53
80
 
54
81
  return result;
@@ -30,10 +30,10 @@ const importDevice = pointer => {
30
30
  * ownership of one reference: the returned texture releases it when destroyed.
31
31
  *
32
32
  * Pair with producers that hand out a +1 reference, such as
33
- * `Skia.Image.MakeNativeTextureFromImage()`:
33
+ * `Skia.Image.MakeGPUTextureFromImage()`:
34
34
  *
35
35
  * ```ts
36
- * const texture = adoptTexture(Skia.Image.MakeNativeTextureFromImage(image));
36
+ * const texture = adoptTexture(Skia.Image.MakeGPUTextureFromImage(image));
37
37
  * ```
38
38
  */
39
39
  exports.importDevice = importDevice;
@@ -1 +1 @@
1
- {"version":3,"names":["_main","require","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","_constants","_install"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;;;;;;;;;;;;;AAcA,IAAAA,KAAA,GAAAC,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAH,KAAA,EAAAI,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAC,YAAA,EAAAJ,GAAA;EAAA,IAAAA,GAAA,IAAAK,OAAA,IAAAA,OAAA,CAAAL,GAAA,MAAAL,KAAA,CAAAK,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAb,KAAA,CAAAK,GAAA;IAAA;EAAA;AAAA;AACA,IAAAS,UAAA,GAAAb,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAW,UAAA,EAAAV,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAC,YAAA,EAAAJ,GAAA;EAAA,IAAAA,GAAA,IAAAK,OAAA,IAAAA,OAAA,CAAAL,GAAA,MAAAS,UAAA,CAAAT,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAC,UAAA,CAAAT,GAAA;IAAA;EAAA;AAAA;AACA,IAAAU,QAAA,GAAAd,OAAA","ignoreList":[]}
1
+ {"version":3,"names":["_main","require","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","_constants","_install"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;;;;;;;;;;;;;AAkBA,IAAAA,KAAA,GAAAC,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAH,KAAA,EAAAI,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAC,YAAA,EAAAJ,GAAA;EAAA,IAAAA,GAAA,IAAAK,OAAA,IAAAA,OAAA,CAAAL,GAAA,MAAAL,KAAA,CAAAK,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAb,KAAA,CAAAK,GAAA;IAAA;EAAA;AAAA;AACA,IAAAS,UAAA,GAAAb,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAW,UAAA,EAAAV,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAC,YAAA,EAAAJ,GAAA;EAAA,IAAAA,GAAA,IAAAK,OAAA,IAAAA,OAAA,CAAAL,GAAA,MAAAS,UAAA,CAAAT,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAC,UAAA,CAAAT,GAAA;IAAA;EAAA;AAAA;AACA,IAAAU,QAAA,GAAAd,OAAA","ignoreList":[]}