react-native-webgpu 0.5.18 → 0.6.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 +24 -9
- package/android/cpp/cpp-adapter.cpp +35 -13
- package/android/src/main/java/com/webgpu/WebGPUAHBView.java +2 -2
- package/android/src/main/java/com/webgpu/WebGPUAPI.java +7 -2
- package/android/src/main/java/com/webgpu/WebGPUSurfaceView.java +3 -1
- package/android/src/main/java/com/webgpu/WebGPUTextureView.java +11 -7
- package/android/src/main/java/com/webgpu/WebGPUView.java +10 -7
- package/android/src/main/java/com/webgpu/WebGPUViewManager.java +6 -0
- package/apple/MetalView.mm +33 -8
- package/cpp/rnwgpu/RNWebGPUManager.cpp +14 -1
- package/cpp/rnwgpu/RNWebGPUManager.h +9 -0
- package/cpp/rnwgpu/SurfaceRegistry.h +469 -131
- package/cpp/rnwgpu/api/GPUAdapter.cpp +43 -7
- package/cpp/rnwgpu/api/GPUAdapter.h +5 -4
- package/cpp/rnwgpu/api/GPUBuffer.cpp +12 -2
- package/cpp/rnwgpu/api/GPUBuffer.h +3 -2
- package/cpp/rnwgpu/api/GPUCanvasContext.cpp +33 -19
- package/cpp/rnwgpu/api/GPUCanvasContext.h +3 -1
- package/cpp/rnwgpu/api/GPUDevice.cpp +32 -17
- package/cpp/rnwgpu/api/GPUDevice.h +9 -7
- package/cpp/rnwgpu/api/GPUExternalTexture.cpp +105 -74
- package/cpp/rnwgpu/api/GPUExternalTexture.h +21 -2
- package/cpp/rnwgpu/api/GPUQueue.cpp +6 -2
- package/cpp/rnwgpu/api/GPUQueue.h +3 -3
- package/cpp/rnwgpu/api/GPUShaderModule.cpp +7 -2
- package/cpp/rnwgpu/api/GPUShaderModule.h +3 -3
- package/cpp/rnwgpu/api/RNWebGPU.h +22 -1
- package/cpp/rnwgpu/async/RuntimeContext.cpp +8 -1
- package/cpp/rnwgpu/async/RuntimeContext.h +9 -2
- package/lib/commonjs/Canvas.js +10 -0
- package/lib/commonjs/Canvas.js.map +1 -1
- package/lib/commonjs/WebPolyfillGPUModule.js +2 -0
- package/lib/commonjs/WebPolyfillGPUModule.js.map +1 -1
- package/lib/module/Canvas.js +11 -1
- package/lib/module/Canvas.js.map +1 -1
- package/lib/module/WebPolyfillGPUModule.js +2 -0
- package/lib/module/WebPolyfillGPUModule.js.map +1 -1
- package/lib/typescript/lib/commonjs/Canvas.d.ts.map +1 -1
- package/lib/typescript/lib/module/Canvas.d.ts.map +1 -1
- package/lib/typescript/src/Canvas.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +4 -6
- package/src/Canvas.tsx +11 -1
- package/src/WebPolyfillGPUModule.ts +2 -0
- package/src/index.tsx +12 -0
- package/android/src/main/java/com/webgpu/WebGPUBaseView.java +0 -61
|
@@ -2,12 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
#include <algorithm>
|
|
4
4
|
#include <cstdio>
|
|
5
|
+
#include <cstring>
|
|
5
6
|
#include <memory>
|
|
6
7
|
#include <string>
|
|
7
8
|
#include <unordered_set>
|
|
8
9
|
#include <utility>
|
|
9
10
|
#include <vector>
|
|
10
11
|
|
|
12
|
+
#ifdef __APPLE__
|
|
13
|
+
#include <TargetConditionals.h>
|
|
14
|
+
#endif
|
|
15
|
+
|
|
11
16
|
#include "Convertors.h"
|
|
12
17
|
|
|
13
18
|
#include "GPUFeatures.h"
|
|
@@ -18,6 +23,7 @@
|
|
|
18
23
|
namespace rnwgpu {
|
|
19
24
|
|
|
20
25
|
async::AsyncTaskHandle GPUAdapter::requestDevice(
|
|
26
|
+
jsi::Runtime &runtime,
|
|
21
27
|
std::optional<std::shared_ptr<GPUDeviceDescriptor>> descriptor) {
|
|
22
28
|
// Enable the react-native-wgpu "native-texture" umbrella by default, mirroring
|
|
23
29
|
// the web where importExternalTexture is core and needs no feature request.
|
|
@@ -130,9 +136,15 @@ async::AsyncTaskHandle GPUAdapter::requestDevice(
|
|
|
130
136
|
descriptor.has_value() ? descriptor.value()->label.value_or("") : "";
|
|
131
137
|
|
|
132
138
|
auto creationRuntime = getCreationRuntime();
|
|
133
|
-
|
|
139
|
+
// Post to the CALLING runtime's context so the promise settles on the
|
|
140
|
+
// thread that requested it (see GPUBuffer::mapAsync). The GPUDevice is also
|
|
141
|
+
// bound to this context, honoring the contract that a device belongs to the
|
|
142
|
+
// runtime that requested it.
|
|
143
|
+
auto context =
|
|
144
|
+
async::RuntimeContext::getOrCreate(runtime, _async->instance());
|
|
145
|
+
return context->postTask(
|
|
134
146
|
[this, aDescriptor, descriptor, label = std::move(label),
|
|
135
|
-
deviceLostBinding,
|
|
147
|
+
deviceLostBinding, context,
|
|
136
148
|
creationRuntime](const async::AsyncTaskHandle::ResolveFunction &resolve,
|
|
137
149
|
const async::AsyncTaskHandle::RejectFunction &reject) {
|
|
138
150
|
// Build a local mutable copy so we can chain Dawn's device toggles.
|
|
@@ -150,21 +162,45 @@ async::AsyncTaskHandle GPUAdapter::requestDevice(
|
|
|
150
162
|
for (const auto &t : dawnToggles->enabledToggles.value()) {
|
|
151
163
|
enabledToggles.push_back(t.c_str());
|
|
152
164
|
}
|
|
153
|
-
toggles.enabledToggleCount = enabledToggles.size();
|
|
154
|
-
toggles.enabledToggles = enabledToggles.data();
|
|
155
165
|
}
|
|
156
166
|
if (dawnToggles->disabledToggles) {
|
|
157
167
|
for (const auto &t : dawnToggles->disabledToggles.value()) {
|
|
158
168
|
disabledToggles.push_back(t.c_str());
|
|
159
169
|
}
|
|
160
|
-
toggles.disabledToggleCount = disabledToggles.size();
|
|
161
|
-
toggles.disabledToggles = disabledToggles.data();
|
|
162
170
|
}
|
|
171
|
+
}
|
|
172
|
+
// TODO: in the latest version of Dawn, this won't be needed (https://issues.chromium.org/issues/42241591)
|
|
173
|
+
#if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR
|
|
174
|
+
// The iOS Simulator only advertises MTLFeatureSet_iOS_GPUFamily2, so
|
|
175
|
+
// Dawn defaults disable_base_instance/disable_base_vertex on and then
|
|
176
|
+
// rejects every draw with a non-zero firstInstance or baseVertex,
|
|
177
|
+
// both core WebGPU. The simulator forwards Metal calls to the host
|
|
178
|
+
// GPU, which does support base vertex/instance drawing, so force the
|
|
179
|
+
// toggles off. Device builds are unaffected: WebGPU-capable iPhones
|
|
180
|
+
// and iPads are all GPUFamily3+. These are device-stage toggles, so
|
|
181
|
+
// they must be chained here rather than on the instance descriptor
|
|
182
|
+
// (instance-stage toggle parsing silently drops them).
|
|
183
|
+
static const char *const kSimulatorDisabledToggles[] = {
|
|
184
|
+
"disable_base_instance", "disable_base_vertex"};
|
|
185
|
+
for (const char *name : kSimulatorDisabledToggles) {
|
|
186
|
+
const bool explicitlyEnabled = std::any_of(
|
|
187
|
+
enabledToggles.begin(), enabledToggles.end(),
|
|
188
|
+
[name](const char *t) { return std::strcmp(t, name) == 0; });
|
|
189
|
+
if (!explicitlyEnabled) {
|
|
190
|
+
disabledToggles.push_back(name);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
#endif
|
|
194
|
+
if (!enabledToggles.empty() || !disabledToggles.empty()) {
|
|
195
|
+
toggles.enabledToggleCount = enabledToggles.size();
|
|
196
|
+
toggles.enabledToggles = enabledToggles.data();
|
|
197
|
+
toggles.disabledToggleCount = disabledToggles.size();
|
|
198
|
+
toggles.disabledToggles = disabledToggles.data();
|
|
163
199
|
deviceDesc.nextInChain = &toggles;
|
|
164
200
|
}
|
|
165
201
|
_instance.RequestDevice(
|
|
166
202
|
&deviceDesc, wgpu::CallbackMode::AllowProcessEvents,
|
|
167
|
-
[context
|
|
203
|
+
[context, resolve, reject, label, creationRuntime,
|
|
168
204
|
deviceLostBinding](wgpu::RequestDeviceStatus status,
|
|
169
205
|
wgpu::Device device, wgpu::StringView message) {
|
|
170
206
|
if (message.length) {
|
|
@@ -33,8 +33,9 @@ public:
|
|
|
33
33
|
public:
|
|
34
34
|
std::string getBrand() { return CLASS_NAME; }
|
|
35
35
|
|
|
36
|
-
async::AsyncTaskHandle
|
|
37
|
-
|
|
36
|
+
async::AsyncTaskHandle requestDevice(
|
|
37
|
+
jsi::Runtime &runtime,
|
|
38
|
+
std::optional<std::shared_ptr<GPUDeviceDescriptor>> descriptor);
|
|
38
39
|
|
|
39
40
|
std::unordered_set<std::string> getFeatures();
|
|
40
41
|
std::shared_ptr<GPUSupportedLimits> getLimits();
|
|
@@ -42,8 +43,8 @@ public:
|
|
|
42
43
|
|
|
43
44
|
static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) {
|
|
44
45
|
installGetter(runtime, prototype, "__brand", &GPUAdapter::getBrand);
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
installMethodWithRuntime(runtime, prototype, "requestDevice",
|
|
47
|
+
&GPUAdapter::requestDevice);
|
|
47
48
|
installGetter(runtime, prototype, "features", &GPUAdapter::getFeatures);
|
|
48
49
|
installGetter(runtime, prototype, "limits", &GPUAdapter::getLimits);
|
|
49
50
|
installGetter(runtime, prototype, "info", &GPUAdapter::getInfo);
|
|
@@ -37,7 +37,8 @@ GPUBuffer::getMappedRange(std::optional<size_t> o, std::optional<size_t> size) {
|
|
|
37
37
|
|
|
38
38
|
void GPUBuffer::destroy() { _instance.Destroy(); }
|
|
39
39
|
|
|
40
|
-
async::AsyncTaskHandle GPUBuffer::mapAsync(
|
|
40
|
+
async::AsyncTaskHandle GPUBuffer::mapAsync(jsi::Runtime &runtime,
|
|
41
|
+
uint64_t modeIn,
|
|
41
42
|
std::optional<uint64_t> offset,
|
|
42
43
|
std::optional<uint64_t> size) {
|
|
43
44
|
Convertor conv;
|
|
@@ -51,7 +52,16 @@ async::AsyncTaskHandle GPUBuffer::mapAsync(uint64_t modeIn,
|
|
|
51
52
|
auto bufferHandle = _instance;
|
|
52
53
|
uint64_t resolvedOffset = offset.value_or(0);
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
// Post to the CALLING runtime's context, not the one captured at buffer
|
|
56
|
+
// creation (_async): the buffer may have been created on another runtime and
|
|
57
|
+
// boxed across (e.g. device created on the main JS runtime, mapAsync called
|
|
58
|
+
// from a worklet). The returned Promise lives on the calling runtime, so it
|
|
59
|
+
// must be settled from that runtime's own thread — and postTask itself
|
|
60
|
+
// schedules the pump through its context's runtime (setTimeout), which is
|
|
61
|
+
// only safe for the runtime we are currently executing on.
|
|
62
|
+
auto context =
|
|
63
|
+
async::RuntimeContext::getOrCreate(runtime, _async->instance());
|
|
64
|
+
return context->postTask(
|
|
55
65
|
[bufferHandle, mode, resolvedOffset,
|
|
56
66
|
rangeSize](const async::AsyncTaskHandle::ResolveFunction &resolve,
|
|
57
67
|
const async::AsyncTaskHandle::RejectFunction &reject) {
|
|
@@ -33,7 +33,7 @@ public:
|
|
|
33
33
|
public:
|
|
34
34
|
std::string getBrand() { return CLASS_NAME; }
|
|
35
35
|
|
|
36
|
-
async::AsyncTaskHandle mapAsync(uint64_t modeIn,
|
|
36
|
+
async::AsyncTaskHandle mapAsync(jsi::Runtime &runtime, uint64_t modeIn,
|
|
37
37
|
std::optional<uint64_t> offset,
|
|
38
38
|
std::optional<uint64_t> size);
|
|
39
39
|
std::shared_ptr<ArrayBuffer> getMappedRange(std::optional<size_t> offset,
|
|
@@ -53,7 +53,8 @@ public:
|
|
|
53
53
|
|
|
54
54
|
static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) {
|
|
55
55
|
installGetter(runtime, prototype, "__brand", &GPUBuffer::getBrand);
|
|
56
|
-
|
|
56
|
+
installMethodWithRuntime(runtime, prototype, "mapAsync",
|
|
57
|
+
&GPUBuffer::mapAsync);
|
|
57
58
|
installMethod(runtime, prototype, "getMappedRange",
|
|
58
59
|
&GPUBuffer::getMappedRange);
|
|
59
60
|
installMethod(runtime, prototype, "unmap", &GPUBuffer::unmap);
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
#include "GPUCanvasContext.h"
|
|
2
2
|
#include "Convertors.h"
|
|
3
3
|
#include "RNWebGPUManager.h"
|
|
4
|
+
#include <algorithm>
|
|
4
5
|
#include <memory>
|
|
6
|
+
#include <utility>
|
|
7
|
+
#include <vector>
|
|
5
8
|
|
|
6
9
|
namespace rnwgpu {
|
|
7
10
|
|
|
@@ -10,37 +13,50 @@ void GPUCanvasContext::configure(
|
|
|
10
13
|
Convertor conv;
|
|
11
14
|
wgpu::SurfaceConfiguration surfaceConfiguration;
|
|
12
15
|
surfaceConfiguration.device = configuration->device->get();
|
|
13
|
-
if (configuration->viewFormats.has_value()) {
|
|
14
|
-
if (!conv(surfaceConfiguration.viewFormats,
|
|
15
|
-
surfaceConfiguration.viewFormatCount,
|
|
16
|
-
configuration->viewFormats.value())) {
|
|
17
|
-
throw std::runtime_error("Error with SurfaceConfiguration");
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
16
|
if (!conv(surfaceConfiguration.usage, configuration->usage) ||
|
|
21
17
|
!conv(surfaceConfiguration.format, configuration->format)) {
|
|
22
18
|
throw std::runtime_error("Error with SurfaceConfiguration");
|
|
23
19
|
}
|
|
20
|
+
// viewFormats are deep-copied into SurfaceInfo (which outlives this call);
|
|
21
|
+
// Convertor-allocated arrays would dangle.
|
|
22
|
+
std::vector<wgpu::TextureFormat> viewFormats;
|
|
23
|
+
if (configuration->viewFormats.has_value()) {
|
|
24
|
+
viewFormats = configuration->viewFormats.value();
|
|
25
|
+
}
|
|
24
26
|
|
|
25
27
|
#ifdef __APPLE__
|
|
26
28
|
surfaceConfiguration.alphaMode = configuration->alphaMode;
|
|
27
29
|
#endif
|
|
28
30
|
surfaceConfiguration.presentMode = wgpu::PresentMode::Fifo;
|
|
29
|
-
_surfaceInfo->configure(surfaceConfiguration);
|
|
31
|
+
_surfaceInfo->configure(surfaceConfiguration, std::move(viewFormats));
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
void GPUCanvasContext::unconfigure() {}
|
|
34
|
+
void GPUCanvasContext::unconfigure() { _surfaceInfo->unconfigure(); }
|
|
33
35
|
|
|
34
36
|
std::shared_ptr<GPUTexture> GPUCanvasContext::getCurrentTexture() {
|
|
37
|
+
if (!_surfaceInfo->isConfigured()) {
|
|
38
|
+
// Web parity: on the web this is an InvalidStateError, not a crash.
|
|
39
|
+
throw std::runtime_error(
|
|
40
|
+
"[WebGPU] getCurrentTexture() called on a canvas context that is not "
|
|
41
|
+
"configured; call context.configure() first");
|
|
42
|
+
}
|
|
43
|
+
// The drawing buffer tracks canvas.width/height (like on the web); resize it
|
|
44
|
+
// lazily when they changed. Sizes are clamped to 1 so a canvas that has not
|
|
45
|
+
// been laid out yet (0x0) keeps working.
|
|
35
46
|
auto prevSize = _surfaceInfo->getConfig();
|
|
36
|
-
auto width = _canvas->getWidth();
|
|
37
|
-
auto height = _canvas->getHeight();
|
|
38
|
-
auto sizeHasChanged = prevSize.width != width ||
|
|
47
|
+
auto width = std::max(1, _canvas->getWidth());
|
|
48
|
+
auto height = std::max(1, _canvas->getHeight());
|
|
49
|
+
auto sizeHasChanged = prevSize.width != static_cast<uint32_t>(width) ||
|
|
50
|
+
prevSize.height != static_cast<uint32_t>(height);
|
|
39
51
|
if (sizeHasChanged) {
|
|
40
52
|
_surfaceInfo->reconfigure(width, height);
|
|
41
53
|
}
|
|
42
54
|
|
|
43
55
|
auto texture = _surfaceInfo->getCurrentTexture();
|
|
56
|
+
if (texture == nullptr) {
|
|
57
|
+
throw std::runtime_error(
|
|
58
|
+
"[WebGPU] getCurrentTexture() failed to acquire a texture");
|
|
59
|
+
}
|
|
44
60
|
|
|
45
61
|
auto size = _surfaceInfo->getSize();
|
|
46
62
|
_canvas->setClientWidth(size.width);
|
|
@@ -52,13 +68,11 @@ std::shared_ptr<GPUTexture> GPUCanvasContext::getCurrentTexture() {
|
|
|
52
68
|
}
|
|
53
69
|
|
|
54
70
|
void GPUCanvasContext::present() {
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
|
|
60
|
-
_surfaceInfo->presentFrame();
|
|
61
|
-
}
|
|
71
|
+
// presentFrame() is the end-of-frame boundary: it presents when this frame's
|
|
72
|
+
// texture was acquired from the on-screen surface (offscreen and dropped
|
|
73
|
+
// frames are skipped), clears the frame state, and adopts any surface that
|
|
74
|
+
// attached while the frame was in flight.
|
|
75
|
+
_surfaceInfo->presentFrame();
|
|
62
76
|
}
|
|
63
77
|
|
|
64
78
|
} // namespace rnwgpu
|
|
@@ -57,7 +57,9 @@ public:
|
|
|
57
57
|
std::shared_ptr<GPUTexture> getCurrentTexture();
|
|
58
58
|
// Present is explicit on every runtime (main JS, Reanimated UI, and dedicated
|
|
59
59
|
// worklet runtimes). It runs synchronously on the calling thread, preserving
|
|
60
|
-
// Dawn surface thread-affinity; offscreen
|
|
60
|
+
// Dawn surface thread-affinity; frames rendered offscreen (no surface
|
|
61
|
+
// attached) are skipped. It is also the end-of-frame boundary at which a
|
|
62
|
+
// newly attached native surface is adopted (see SurfaceInfo).
|
|
61
63
|
void present();
|
|
62
64
|
|
|
63
65
|
private:
|
|
@@ -348,6 +348,7 @@ std::shared_ptr<GPUSharedFence> GPUDevice::importSharedFence(
|
|
|
348
348
|
}
|
|
349
349
|
|
|
350
350
|
async::AsyncTaskHandle GPUDevice::createComputePipelineAsync(
|
|
351
|
+
jsi::Runtime &runtime,
|
|
351
352
|
std::shared_ptr<GPUComputePipelineDescriptor> descriptor) {
|
|
352
353
|
wgpu::ComputePipelineDescriptor desc{};
|
|
353
354
|
Convertor conv;
|
|
@@ -360,12 +361,16 @@ async::AsyncTaskHandle GPUDevice::createComputePipelineAsync(
|
|
|
360
361
|
descriptor->label.has_value() ? descriptor->label.value() : "");
|
|
361
362
|
auto pipelineHolder = std::make_shared<GPUComputePipeline>(nullptr, label);
|
|
362
363
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
364
|
+
// Post to the CALLING runtime's context so the promise settles on the
|
|
365
|
+
// thread that requested it (see GPUBuffer::mapAsync).
|
|
366
|
+
auto context =
|
|
367
|
+
async::RuntimeContext::getOrCreate(runtime, _async->instance());
|
|
368
|
+
return context->postTask([device = _instance, desc, descriptor,
|
|
369
|
+
pipelineHolder](
|
|
370
|
+
const async::AsyncTaskHandle::ResolveFunction
|
|
371
|
+
&resolve,
|
|
372
|
+
const async::AsyncTaskHandle::RejectFunction
|
|
373
|
+
&reject) {
|
|
369
374
|
(void)descriptor;
|
|
370
375
|
device.CreateComputePipelineAsync(
|
|
371
376
|
&desc, wgpu::CallbackMode::AllowProcessEvents,
|
|
@@ -389,6 +394,7 @@ async::AsyncTaskHandle GPUDevice::createComputePipelineAsync(
|
|
|
389
394
|
}
|
|
390
395
|
|
|
391
396
|
async::AsyncTaskHandle GPUDevice::createRenderPipelineAsync(
|
|
397
|
+
jsi::Runtime &runtime,
|
|
392
398
|
std::shared_ptr<GPURenderPipelineDescriptor> descriptor) {
|
|
393
399
|
wgpu::RenderPipelineDescriptor desc{};
|
|
394
400
|
Convertor conv;
|
|
@@ -402,12 +408,16 @@ async::AsyncTaskHandle GPUDevice::createRenderPipelineAsync(
|
|
|
402
408
|
descriptor->label.has_value() ? descriptor->label.value() : "");
|
|
403
409
|
auto pipelineHolder = std::make_shared<GPURenderPipeline>(nullptr, label);
|
|
404
410
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
+
// Post to the CALLING runtime's context so the promise settles on the
|
|
412
|
+
// thread that requested it (see GPUBuffer::mapAsync).
|
|
413
|
+
auto context =
|
|
414
|
+
async::RuntimeContext::getOrCreate(runtime, _async->instance());
|
|
415
|
+
return context->postTask([device = _instance, desc, descriptor,
|
|
416
|
+
pipelineHolder](
|
|
417
|
+
const async::AsyncTaskHandle::ResolveFunction
|
|
418
|
+
&resolve,
|
|
419
|
+
const async::AsyncTaskHandle::RejectFunction
|
|
420
|
+
&reject) {
|
|
411
421
|
(void)descriptor;
|
|
412
422
|
device.CreateRenderPipelineAsync(
|
|
413
423
|
&desc, wgpu::CallbackMode::AllowProcessEvents,
|
|
@@ -433,13 +443,18 @@ void GPUDevice::pushErrorScope(wgpu::ErrorFilter filter) {
|
|
|
433
443
|
_instance.PushErrorScope(filter);
|
|
434
444
|
}
|
|
435
445
|
|
|
436
|
-
async::AsyncTaskHandle GPUDevice::popErrorScope() {
|
|
446
|
+
async::AsyncTaskHandle GPUDevice::popErrorScope(jsi::Runtime &runtime) {
|
|
437
447
|
auto device = _instance;
|
|
438
448
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
449
|
+
// Post to the CALLING runtime's context so the promise settles on the
|
|
450
|
+
// thread that requested it (see GPUBuffer::mapAsync).
|
|
451
|
+
auto context =
|
|
452
|
+
async::RuntimeContext::getOrCreate(runtime, _async->instance());
|
|
453
|
+
return context->postTask([device](
|
|
454
|
+
const async::AsyncTaskHandle::ResolveFunction
|
|
455
|
+
&resolve,
|
|
456
|
+
const async::AsyncTaskHandle::RejectFunction
|
|
457
|
+
&reject) {
|
|
443
458
|
device.PopErrorScope(
|
|
444
459
|
wgpu::CallbackMode::AllowProcessEvents,
|
|
445
460
|
[resolve, reject](wgpu::PopErrorScopeStatus status,
|
|
@@ -136,8 +136,10 @@ public:
|
|
|
136
136
|
std::shared_ptr<GPURenderPipeline>
|
|
137
137
|
createRenderPipeline(std::shared_ptr<GPURenderPipelineDescriptor> descriptor);
|
|
138
138
|
async::AsyncTaskHandle createComputePipelineAsync(
|
|
139
|
+
jsi::Runtime &runtime,
|
|
139
140
|
std::shared_ptr<GPUComputePipelineDescriptor> descriptor);
|
|
140
141
|
async::AsyncTaskHandle createRenderPipelineAsync(
|
|
142
|
+
jsi::Runtime &runtime,
|
|
141
143
|
std::shared_ptr<GPURenderPipelineDescriptor> descriptor);
|
|
142
144
|
std::shared_ptr<GPUCommandEncoder> createCommandEncoder(
|
|
143
145
|
std::optional<std::shared_ptr<GPUCommandEncoderDescriptor>> descriptor);
|
|
@@ -146,7 +148,7 @@ public:
|
|
|
146
148
|
std::shared_ptr<GPUQuerySet>
|
|
147
149
|
createQuerySet(std::shared_ptr<GPUQuerySetDescriptor> descriptor);
|
|
148
150
|
void pushErrorScope(wgpu::ErrorFilter filter);
|
|
149
|
-
async::AsyncTaskHandle popErrorScope();
|
|
151
|
+
async::AsyncTaskHandle popErrorScope(jsi::Runtime &runtime);
|
|
150
152
|
|
|
151
153
|
std::unordered_set<std::string> getFeatures();
|
|
152
154
|
std::shared_ptr<GPUSupportedLimits> getLimits();
|
|
@@ -192,10 +194,10 @@ public:
|
|
|
192
194
|
&GPUDevice::createComputePipeline);
|
|
193
195
|
installMethod(runtime, prototype, "createRenderPipeline",
|
|
194
196
|
&GPUDevice::createRenderPipeline);
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
197
|
+
installMethodWithRuntime(runtime, prototype, "createComputePipelineAsync",
|
|
198
|
+
&GPUDevice::createComputePipelineAsync);
|
|
199
|
+
installMethodWithRuntime(runtime, prototype, "createRenderPipelineAsync",
|
|
200
|
+
&GPUDevice::createRenderPipelineAsync);
|
|
199
201
|
installMethod(runtime, prototype, "createCommandEncoder",
|
|
200
202
|
&GPUDevice::createCommandEncoder);
|
|
201
203
|
installMethod(runtime, prototype, "createRenderBundleEncoder",
|
|
@@ -204,8 +206,8 @@ public:
|
|
|
204
206
|
&GPUDevice::createQuerySet);
|
|
205
207
|
installMethod(runtime, prototype, "pushErrorScope",
|
|
206
208
|
&GPUDevice::pushErrorScope);
|
|
207
|
-
|
|
208
|
-
|
|
209
|
+
installMethodWithRuntime(runtime, prototype, "popErrorScope",
|
|
210
|
+
&GPUDevice::popErrorScope);
|
|
209
211
|
installGetter(runtime, prototype, "features", &GPUDevice::getFeatures);
|
|
210
212
|
installGetter(runtime, prototype, "limits", &GPUDevice::getLimits);
|
|
211
213
|
installGetter(runtime, prototype, "queue", &GPUDevice::getQueue);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#include "GPUExternalTexture.h"
|
|
2
2
|
|
|
3
|
+
#include <array>
|
|
3
4
|
#include <cmath>
|
|
4
5
|
#include <memory>
|
|
5
6
|
#include <string>
|
|
@@ -56,44 +57,76 @@ static const float kIdentityTransferParams[7] = {
|
|
|
56
57
|
0.0f, // F
|
|
57
58
|
};
|
|
58
59
|
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// range if it matters).
|
|
68
|
-
[[maybe_unused]] static const float kBT709LimitedToRgb[12] = {
|
|
69
|
-
1.164383f, 0.000000f, 1.792741f, -0.972945f, //
|
|
70
|
-
1.164383f, -0.213249f, -0.532909f, 0.301517f, //
|
|
71
|
-
1.164383f, 2.112402f, 0.000000f, -1.133402f, //
|
|
60
|
+
// Identity passthrough for GPUExternalTexture.yuvToRgbMatrix: the sampled
|
|
61
|
+
// texel is already RGB (Apple's biplanar path where Dawn converts in the
|
|
62
|
+
// sampler transform, RGBA surfaces, or an Android AHB whose driver reports
|
|
63
|
+
// RGB_IDENTITY). A 3x4 row-major matrix mapping [c.r, c.g, c.b, 1] to itself.
|
|
64
|
+
static constexpr std::array<float, 12> kYuvPassthroughMatrix = {
|
|
65
|
+
1.0f, 0.0f, 0.0f, 0.0f, //
|
|
66
|
+
0.0f, 1.0f, 0.0f, 0.0f, //
|
|
67
|
+
0.0f, 0.0f, 1.0f, 0.0f, //
|
|
72
68
|
};
|
|
73
69
|
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
70
|
+
// Build a 3x4 row-major matrix mapping the *sampled* gamma-encoded
|
|
71
|
+
// [Y, Cb, Cr, 1] (each normalized to [0,1]) to gamma-encoded R'G'B', from the
|
|
72
|
+
// Vulkan-suggested YCbCr model + range of an Android external-format buffer.
|
|
73
|
+
// Needed because Dawn's opaque-YCbCr sampling path hard-codes an RGB_IDENTITY
|
|
74
|
+
// conversion (SamplerVk.cpp::GetYCbCrForTextureView, crbug.com/497675620), so
|
|
75
|
+
// the shader receives raw Y/Cb/Cr and must convert itself.
|
|
76
|
+
// The VkSamplerYcbcrModelConversion / VkSamplerYcbcrRange values are inlined
|
|
77
|
+
// to avoid a Vulkan header dependency in this cross-platform file.
|
|
78
|
+
[[maybe_unused]] static std::array<float, 12>
|
|
79
|
+
makeYuvToRgbMatrix(uint32_t vkModel, uint32_t vkRange) {
|
|
80
|
+
constexpr uint32_t kModelRgbIdentity = 0; // VK_..._RGB_IDENTITY
|
|
81
|
+
constexpr uint32_t kModel709 = 2; // VK_..._YCBCR_709
|
|
82
|
+
constexpr uint32_t kModel601 = 3; // VK_..._YCBCR_601
|
|
83
|
+
constexpr uint32_t kModel2020 = 4; // VK_..._YCBCR_2020
|
|
84
|
+
constexpr uint32_t kRangeItuNarrow = 1; // VK_SAMPLER_YCBCR_RANGE_ITU_NARROW
|
|
85
|
+
|
|
86
|
+
if (vkModel == kModelRgbIdentity) {
|
|
87
|
+
// The buffer content is RGB; nothing to convert.
|
|
88
|
+
return kYuvPassthroughMatrix;
|
|
89
|
+
}
|
|
90
|
+
float kr;
|
|
91
|
+
float kb;
|
|
92
|
+
switch (vkModel) {
|
|
93
|
+
case kModel709:
|
|
94
|
+
kr = 0.2126f;
|
|
95
|
+
kb = 0.0722f;
|
|
96
|
+
break;
|
|
97
|
+
case kModel2020:
|
|
98
|
+
kr = 0.2627f;
|
|
99
|
+
kb = 0.0593f;
|
|
100
|
+
break;
|
|
101
|
+
case kModel601:
|
|
87
102
|
default:
|
|
88
|
-
|
|
103
|
+
// YCBCR_IDENTITY and unknown models: Android camera streams are BT.601 in
|
|
104
|
+
// practice, so that is the sane default.
|
|
105
|
+
kr = 0.299f;
|
|
106
|
+
kb = 0.114f;
|
|
107
|
+
break;
|
|
89
108
|
}
|
|
109
|
+
const float kg = 1.0f - kr - kb;
|
|
110
|
+
const bool narrow = vkRange == kRangeItuNarrow;
|
|
111
|
+
const float yScale = narrow ? 255.0f / 219.0f : 1.0f;
|
|
112
|
+
const float cScale = narrow ? 255.0f / 224.0f : 1.0f;
|
|
113
|
+
const float yOffset = narrow ? 16.0f / 255.0f : 0.0f;
|
|
114
|
+
const float cOffset = 128.0f / 255.0f;
|
|
115
|
+
const float crR = 2.0f * (1.0f - kr) * cScale;
|
|
116
|
+
const float cbG = -2.0f * kb * (1.0f - kb) / kg * cScale;
|
|
117
|
+
const float crG = -2.0f * kr * (1.0f - kr) / kg * cScale;
|
|
118
|
+
const float cbB = 2.0f * (1.0f - kb) * cScale;
|
|
119
|
+
return {
|
|
120
|
+
yScale, 0.0f, crR, -yScale * yOffset - crR * cOffset, //
|
|
121
|
+
yScale, cbG, crG, -yScale * yOffset - (cbG + crG) * cOffset, //
|
|
122
|
+
yScale, cbB, 0.0f, -yScale * yOffset - cbB * cOffset, //
|
|
123
|
+
};
|
|
90
124
|
}
|
|
91
125
|
|
|
92
126
|
// Map a rotation in degrees (0 / 90 / 180 / 270) to Dawn's enum. Anything that
|
|
93
127
|
// isn't a clean multiple of 90 snaps to the nearest quadrant; Dawn only
|
|
94
128
|
// supports those four steps for external textures.
|
|
95
|
-
static wgpu::ExternalTextureRotation
|
|
96
|
-
toExternalTextureRotation(double degrees) {
|
|
129
|
+
static wgpu::ExternalTextureRotation toExternalTextureRotation(double degrees) {
|
|
97
130
|
int quadrant = static_cast<int>(std::lround(degrees / 90.0)) & 3;
|
|
98
131
|
switch (quadrant) {
|
|
99
132
|
case 1:
|
|
@@ -216,7 +249,11 @@ std::shared_ptr<GPUExternalTexture> GPUExternalTexture::Create(
|
|
|
216
249
|
|
|
217
250
|
return std::make_shared<GPUExternalTexture>(
|
|
218
251
|
std::move(external), std::move(memory), std::move(texture),
|
|
219
|
-
std::move(descriptor->source), std::move(label)
|
|
252
|
+
std::move(descriptor->source), std::move(label),
|
|
253
|
+
// Dawn's Metal path applies the YUV->RGB conversion (from
|
|
254
|
+
// frame.yuvToRgbMatrix above) inside the sampling transform, so the
|
|
255
|
+
// sampled texel is already RGB.
|
|
256
|
+
kYuvPassthroughMatrix);
|
|
220
257
|
#elif defined(__ANDROID__)
|
|
221
258
|
// 1. Import the AHardwareBuffer as SharedTextureMemory. For YUV AHBs this
|
|
222
259
|
// yields a Dawn texture in the implementation-defined OpaqueYCbCrAndroid
|
|
@@ -257,32 +294,39 @@ std::shared_ptr<GPUExternalTexture> GPUExternalTexture::Create(
|
|
|
257
294
|
"GPUExternalTexture::Create(): BeginAccess failed");
|
|
258
295
|
}
|
|
259
296
|
|
|
260
|
-
// 4.
|
|
261
|
-
//
|
|
262
|
-
//
|
|
263
|
-
// a
|
|
264
|
-
//
|
|
265
|
-
//
|
|
266
|
-
//
|
|
267
|
-
//
|
|
268
|
-
//
|
|
269
|
-
//
|
|
270
|
-
//
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
297
|
+
// 4. Resolve the YUV->RGB conversion the *shader* must apply. Dawn imports
|
|
298
|
+
// every YUV / implementation-defined AHB format as OpaqueYCbCrAndroid
|
|
299
|
+
// (AHBFunctions.cpp::FormatFromAHardwareBufferFormat) and samples it
|
|
300
|
+
// through a Vulkan SamplerYcbcrConversion that is hard-coded to
|
|
301
|
+
// RGB_IDENTITY (SamplerVk.cpp::GetYCbCrForTextureView,
|
|
302
|
+
// crbug.com/497675620), so the sample always comes back as raw
|
|
303
|
+
// [Y, Cb, Cr]. The driver's *suggested* model + range are captured at
|
|
304
|
+
// import time though; read them back from the shared memory's properties
|
|
305
|
+
// and derive the correct conversion matrix, exposed to JS as
|
|
306
|
+
// GPUExternalTexture.yuvToRgbMatrix. Defined color formats (e.g. an RGBA
|
|
307
|
+
// AHB -> RGBA8Unorm) sample as RGB directly and get the passthrough.
|
|
308
|
+
std::array<float, 12> yuvToRgbMatrix = kYuvPassthroughMatrix;
|
|
309
|
+
if (texture.GetFormat() == wgpu::TextureFormat::OpaqueYCbCrAndroid) {
|
|
310
|
+
wgpu::SharedTextureMemoryAHardwareBufferProperties ahbProps{};
|
|
311
|
+
wgpu::SharedTextureMemoryProperties props{};
|
|
312
|
+
props.nextInChain = &ahbProps;
|
|
313
|
+
if (memory.GetProperties(&props)) {
|
|
314
|
+
yuvToRgbMatrix = makeYuvToRgbMatrix(ahbProps.yCbCrInfo.vkYCbCrModel,
|
|
315
|
+
ahbProps.yCbCrInfo.vkYCbCrRange);
|
|
316
|
+
} else {
|
|
317
|
+
// Fall back to the Android camera norm rather than passthrough.
|
|
318
|
+
yuvToRgbMatrix = makeYuvToRgbMatrix(/* YCBCR_601 */ 3,
|
|
319
|
+
/* ITU_NARROW */ 1);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
283
322
|
|
|
284
|
-
|
|
285
|
-
|
|
323
|
+
// 5. Build the ExternalTextureDescriptor: a single (opaque or RGB) plane
|
|
324
|
+
// with identity transfer. The YUV->RGB conversion cannot run inside
|
|
325
|
+
// Dawn's external-texture transform here: the single-plane branch of the
|
|
326
|
+
// Tint transform ignores yuvToRgbConversionMatrix, which is why the
|
|
327
|
+
// matrix is surfaced to the shader instead. The gamut/transfer arrays
|
|
328
|
+
// must still be non-null: ComputeExternalTextureParams dereferences them
|
|
329
|
+
// unconditionally.
|
|
286
330
|
wgpu::ExternalTextureDescriptor extDesc{};
|
|
287
331
|
if (!label.empty()) {
|
|
288
332
|
extDesc.label = wgpu::StringView(label.c_str(), label.size());
|
|
@@ -291,24 +335,10 @@ std::shared_ptr<GPUExternalTexture> GPUExternalTexture::Create(
|
|
|
291
335
|
extDesc.cropSize = {frame.width, frame.height};
|
|
292
336
|
extDesc.apparentSize = {frame.width, frame.height};
|
|
293
337
|
extDesc.gamutConversionMatrix = kIdentityGamutMatrix;
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
wgpu::TextureViewDescriptor v1{};
|
|
299
|
-
v1.aspect = wgpu::TextureAspect::Plane1Only;
|
|
300
|
-
plane1 = texture.CreateView(&v1);
|
|
301
|
-
extDesc.plane0 = plane0;
|
|
302
|
-
extDesc.plane1 = plane1;
|
|
303
|
-
extDesc.yuvToRgbConversionMatrix = kBT709LimitedToRgb;
|
|
304
|
-
extDesc.srcTransferFunctionParameters = kSrgbDecodeParams;
|
|
305
|
-
extDesc.dstTransferFunctionParameters = kSrgbEncodeParams;
|
|
306
|
-
} else {
|
|
307
|
-
plane0 = texture.CreateView();
|
|
308
|
-
extDesc.plane0 = plane0;
|
|
309
|
-
extDesc.srcTransferFunctionParameters = kIdentityTransferParams;
|
|
310
|
-
extDesc.dstTransferFunctionParameters = kIdentityTransferParams;
|
|
311
|
-
}
|
|
338
|
+
wgpu::TextureView plane0 = texture.CreateView();
|
|
339
|
+
extDesc.plane0 = plane0;
|
|
340
|
+
extDesc.srcTransferFunctionParameters = kIdentityTransferParams;
|
|
341
|
+
extDesc.dstTransferFunctionParameters = kIdentityTransferParams;
|
|
312
342
|
extDesc.mirrored = descriptor->mirrored.value_or(false);
|
|
313
343
|
extDesc.rotation =
|
|
314
344
|
toExternalTextureRotation(descriptor->rotation.value_or(0));
|
|
@@ -324,7 +354,8 @@ std::shared_ptr<GPUExternalTexture> GPUExternalTexture::Create(
|
|
|
324
354
|
|
|
325
355
|
return std::make_shared<GPUExternalTexture>(
|
|
326
356
|
std::move(external), std::move(memory), std::move(texture),
|
|
327
|
-
std::move(descriptor->source), std::move(label)
|
|
357
|
+
std::move(descriptor->source), std::move(label),
|
|
358
|
+
std::move(yuvToRgbMatrix));
|
|
328
359
|
#else
|
|
329
360
|
throw std::runtime_error(
|
|
330
361
|
"GPUExternalTexture::Create(): not yet implemented on this "
|