react-native-webgpu 0.5.11 → 0.5.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -0
- package/android/CMakeLists.txt +2 -0
- package/android/cpp/AndroidPlatformContext.h +121 -0
- package/apple/ApplePlatformContext.h +13 -0
- package/apple/ApplePlatformContext.mm +145 -0
- package/apple/AppleVideoPlayer.h +31 -0
- package/apple/AppleVideoPlayer.mm +314 -0
- package/cpp/rnwgpu/ArrayBuffer.h +51 -7
- package/cpp/rnwgpu/PlatformContext.h +81 -0
- package/cpp/rnwgpu/RNWebGPUManager.cpp +12 -0
- package/cpp/rnwgpu/api/Convertors.h +33 -11
- package/cpp/rnwgpu/api/GPU.cpp +27 -0
- package/cpp/rnwgpu/api/GPUAdapter.cpp +109 -33
- package/cpp/rnwgpu/api/GPUDevice.cpp +58 -5
- package/cpp/rnwgpu/api/GPUDevice.h +6 -0
- package/cpp/rnwgpu/api/GPUExternalTexture.cpp +335 -0
- package/cpp/rnwgpu/api/GPUExternalTexture.h +47 -2
- package/cpp/rnwgpu/api/GPUFeatures.h +4 -1
- package/cpp/rnwgpu/api/GPUShaderModule.cpp +2 -3
- package/cpp/rnwgpu/api/GPUSharedTextureMemory.cpp +80 -0
- package/cpp/rnwgpu/api/GPUSharedTextureMemory.h +71 -0
- package/cpp/rnwgpu/api/ImageBitmap.h +8 -0
- package/cpp/rnwgpu/api/RNWebGPU.h +63 -21
- package/cpp/rnwgpu/api/RnFeatures.h +53 -0
- package/cpp/rnwgpu/api/VideoFrame.h +76 -0
- package/cpp/rnwgpu/api/VideoPlayer.h +69 -0
- package/cpp/rnwgpu/api/descriptors/GPUBindGroupEntry.h +4 -1
- package/cpp/rnwgpu/api/descriptors/GPUDawnTogglesDescriptor.h +57 -0
- package/cpp/rnwgpu/api/descriptors/GPUDeviceDescriptor.h +18 -3
- package/cpp/rnwgpu/api/descriptors/GPUExternalTextureDescriptor.h +35 -33
- package/cpp/rnwgpu/api/descriptors/GPUSharedTextureMemoryDescriptor.h +62 -0
- package/cpp/rnwgpu/api/descriptors/Unions.h +10 -0
- package/cpp/webgpu/webgpu.h +78 -17
- package/cpp/webgpu/webgpu_cpp.h +1014 -1249
- package/cpp/webgpu/webgpu_cpp_print.h +99 -10
- 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 +0 -10
- package/lib/typescript/src/Canvas.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +20 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +32 -1
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/libs/android/arm64-v8a/libwebgpu_dawn.so +0 -0
- package/libs/android/armeabi-v7a/libwebgpu_dawn.so +0 -0
- package/libs/android/x86/libwebgpu_dawn.so +0 -0
- package/libs/android/x86_64/libwebgpu_dawn.so +0 -0
- package/libs/apple/libwebgpu_dawn.xcframework/Info.plist +6 -6
- package/libs/apple/libwebgpu_dawn.xcframework/ios-arm64/libwebgpu_dawn.a +0 -0
- package/libs/apple/libwebgpu_dawn.xcframework/ios-arm64_x86_64-simulator/libwebgpu_dawn.a +0 -0
- package/libs/apple/libwebgpu_dawn.xcframework/macos-arm64_x86_64/libwebgpu_dawn.a +0 -0
- package/package.json +3 -3
- package/src/Canvas.tsx +0 -15
- package/src/index.tsx +62 -2
- package/src/types.ts +83 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#include "GPUAdapter.h"
|
|
2
2
|
|
|
3
|
+
#include <algorithm>
|
|
3
4
|
#include <cstdio>
|
|
4
5
|
#include <memory>
|
|
5
6
|
#include <string>
|
|
@@ -11,12 +12,54 @@
|
|
|
11
12
|
|
|
12
13
|
#include "GPUFeatures.h"
|
|
13
14
|
#include "JSIConverter.h"
|
|
15
|
+
#include "RnFeatures.h"
|
|
14
16
|
#include "WGPULogger.h"
|
|
15
17
|
|
|
16
18
|
namespace rnwgpu {
|
|
17
19
|
|
|
18
20
|
async::AsyncTaskHandle GPUAdapter::requestDevice(
|
|
19
21
|
std::optional<std::shared_ptr<GPUDeviceDescriptor>> descriptor) {
|
|
22
|
+
// Enable the react-native-wgpu "native-texture" umbrella by default, mirroring
|
|
23
|
+
// the web where importExternalTexture is core and needs no feature request.
|
|
24
|
+
// We append the umbrella's backing Dawn features to requiredFeatures so the
|
|
25
|
+
// capability is on without the caller listing it. Two rules keep this safe:
|
|
26
|
+
// - All-or-nothing: only inject when the adapter supports *every* backing
|
|
27
|
+
// feature (same semantics as maybeSynthesizeRnNativeTextureFeature). On a
|
|
28
|
+
// web/fallback adapter the backing set is empty or unsupported, so this is
|
|
29
|
+
// a no-op and device creation is unaffected.
|
|
30
|
+
// - Requesting a feature the adapter doesn't support makes RequestDevice
|
|
31
|
+
// fail, hence the support check below.
|
|
32
|
+
// Callers can still pass "rnwebgpu/native-texture" explicitly; the dedupe
|
|
33
|
+
// keeps that idempotent.
|
|
34
|
+
{
|
|
35
|
+
auto backing = rnNativeTextureBackingFeatures();
|
|
36
|
+
if (!backing.empty()) {
|
|
37
|
+
wgpu::SupportedFeatures supported;
|
|
38
|
+
_instance.GetFeatures(&supported);
|
|
39
|
+
std::unordered_set<wgpu::FeatureName> supportedSet(
|
|
40
|
+
supported.features, supported.features + supported.featureCount);
|
|
41
|
+
bool allSupported = std::all_of(
|
|
42
|
+
backing.begin(), backing.end(),
|
|
43
|
+
[&](wgpu::FeatureName f) { return supportedSet.count(f) > 0; });
|
|
44
|
+
if (allSupported) {
|
|
45
|
+
if (!descriptor.has_value()) {
|
|
46
|
+
descriptor = std::make_shared<GPUDeviceDescriptor>();
|
|
47
|
+
}
|
|
48
|
+
auto &desc = descriptor.value();
|
|
49
|
+
if (!desc->requiredFeatures.has_value()) {
|
|
50
|
+
desc->requiredFeatures = std::vector<wgpu::FeatureName>{};
|
|
51
|
+
}
|
|
52
|
+
auto &features = desc->requiredFeatures.value();
|
|
53
|
+
for (auto f : backing) {
|
|
54
|
+
if (std::find(features.begin(), features.end(), f) ==
|
|
55
|
+
features.end()) {
|
|
56
|
+
features.push_back(f);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
20
63
|
wgpu::DeviceDescriptor aDescriptor;
|
|
21
64
|
Convertor conv;
|
|
22
65
|
if (!conv(aDescriptor, descriptor)) {
|
|
@@ -92,13 +135,39 @@ async::AsyncTaskHandle GPUAdapter::requestDevice(
|
|
|
92
135
|
deviceLostBinding,
|
|
93
136
|
creationRuntime](const async::AsyncTaskHandle::ResolveFunction &resolve,
|
|
94
137
|
const async::AsyncTaskHandle::RejectFunction &reject) {
|
|
95
|
-
|
|
138
|
+
// Build a local mutable copy so we can chain Dawn's device toggles.
|
|
139
|
+
// The toggle name strings are owned by `descriptor` (captured above),
|
|
140
|
+
// and the const char* / DawnTogglesDescriptor locals live for the
|
|
141
|
+
// whole synchronous RequestDevice call below, which is when Dawn reads
|
|
142
|
+
// the chained struct.
|
|
143
|
+
wgpu::DeviceDescriptor deviceDesc = aDescriptor;
|
|
144
|
+
wgpu::DawnTogglesDescriptor toggles{};
|
|
145
|
+
std::vector<const char *> enabledToggles;
|
|
146
|
+
std::vector<const char *> disabledToggles;
|
|
147
|
+
if (descriptor.has_value() && descriptor.value()->dawnToggles) {
|
|
148
|
+
const auto &dawnToggles = descriptor.value()->dawnToggles.value();
|
|
149
|
+
if (dawnToggles->enabledToggles) {
|
|
150
|
+
for (const auto &t : dawnToggles->enabledToggles.value()) {
|
|
151
|
+
enabledToggles.push_back(t.c_str());
|
|
152
|
+
}
|
|
153
|
+
toggles.enabledToggleCount = enabledToggles.size();
|
|
154
|
+
toggles.enabledToggles = enabledToggles.data();
|
|
155
|
+
}
|
|
156
|
+
if (dawnToggles->disabledToggles) {
|
|
157
|
+
for (const auto &t : dawnToggles->disabledToggles.value()) {
|
|
158
|
+
disabledToggles.push_back(t.c_str());
|
|
159
|
+
}
|
|
160
|
+
toggles.disabledToggleCount = disabledToggles.size();
|
|
161
|
+
toggles.disabledToggles = disabledToggles.data();
|
|
162
|
+
}
|
|
163
|
+
deviceDesc.nextInChain = &toggles;
|
|
164
|
+
}
|
|
96
165
|
_instance.RequestDevice(
|
|
97
|
-
&
|
|
166
|
+
&deviceDesc, wgpu::CallbackMode::AllowProcessEvents,
|
|
98
167
|
[asyncRunner = _async, resolve, reject, label, creationRuntime,
|
|
99
168
|
deviceLostBinding](wgpu::RequestDeviceStatus status,
|
|
100
169
|
wgpu::Device device,
|
|
101
|
-
wgpu::StringView message)
|
|
170
|
+
wgpu::StringView message) {
|
|
102
171
|
if (message.length) {
|
|
103
172
|
fprintf(stderr, "%s", message.data);
|
|
104
173
|
}
|
|
@@ -111,36 +180,40 @@ async::AsyncTaskHandle GPUAdapter::requestDevice(
|
|
|
111
180
|
return;
|
|
112
181
|
}
|
|
113
182
|
|
|
114
|
-
device.SetLoggingCallback(
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
183
|
+
device.SetLoggingCallback(
|
|
184
|
+
[](wgpu::LoggingType type, wgpu::StringView msg,
|
|
185
|
+
jsi::Runtime *creationRuntime) {
|
|
186
|
+
if (creationRuntime == nullptr) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const char *logLevel = "";
|
|
190
|
+
switch (type) {
|
|
191
|
+
case wgpu::LoggingType::Warning:
|
|
192
|
+
logLevel = "Warning";
|
|
193
|
+
Logger::warnToJavascriptConsole(
|
|
194
|
+
*creationRuntime,
|
|
195
|
+
std::string(msg.data, msg.length));
|
|
196
|
+
break;
|
|
197
|
+
case wgpu::LoggingType::Error:
|
|
198
|
+
logLevel = "Error";
|
|
199
|
+
Logger::errorToJavascriptConsole(
|
|
200
|
+
*creationRuntime,
|
|
201
|
+
std::string(msg.data, msg.length));
|
|
202
|
+
break;
|
|
203
|
+
case wgpu::LoggingType::Verbose:
|
|
204
|
+
logLevel = "Verbose";
|
|
205
|
+
break;
|
|
206
|
+
case wgpu::LoggingType::Info:
|
|
207
|
+
logLevel = "Info";
|
|
208
|
+
break;
|
|
209
|
+
default:
|
|
210
|
+
logLevel = "Unknown";
|
|
211
|
+
Logger::logToConsole("%s: %.*s", logLevel,
|
|
212
|
+
static_cast<int>(msg.length),
|
|
213
|
+
msg.data);
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
creationRuntime);
|
|
144
217
|
|
|
145
218
|
auto deviceHost = std::make_shared<GPUDevice>(std::move(device),
|
|
146
219
|
asyncRunner, label);
|
|
@@ -163,14 +236,17 @@ std::unordered_set<std::string> GPUAdapter::getFeatures() {
|
|
|
163
236
|
wgpu::SupportedFeatures supportedFeatures;
|
|
164
237
|
_instance.GetFeatures(&supportedFeatures);
|
|
165
238
|
std::unordered_set<std::string> result;
|
|
239
|
+
std::unordered_set<wgpu::FeatureName> enabled;
|
|
166
240
|
for (size_t i = 0; i < supportedFeatures.featureCount; ++i) {
|
|
167
241
|
auto feature = supportedFeatures.features[i];
|
|
242
|
+
enabled.insert(feature);
|
|
168
243
|
std::string name;
|
|
169
244
|
convertEnumToJSUnion(feature, &name);
|
|
170
245
|
if (name != "") {
|
|
171
246
|
result.insert(name);
|
|
172
247
|
}
|
|
173
248
|
}
|
|
249
|
+
maybeSynthesizeRnNativeTextureFeature(enabled, result);
|
|
174
250
|
return result;
|
|
175
251
|
}
|
|
176
252
|
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
#include "GPUInternalError.h"
|
|
14
14
|
#include "GPUOutOfMemoryError.h"
|
|
15
15
|
#include "GPUValidationError.h"
|
|
16
|
+
#include "RnFeatures.h"
|
|
16
17
|
|
|
17
18
|
namespace rnwgpu {
|
|
18
19
|
|
|
@@ -234,8 +235,55 @@ std::shared_ptr<GPUPipelineLayout> GPUDevice::createPipelineLayout(
|
|
|
234
235
|
|
|
235
236
|
std::shared_ptr<GPUExternalTexture> GPUDevice::importExternalTexture(
|
|
236
237
|
std::shared_ptr<GPUExternalTextureDescriptor> descriptor) {
|
|
238
|
+
// The import / begin-access / descriptor-build logic, plus the matching
|
|
239
|
+
// EndAccess, all live on GPUExternalTexture so the begin/end lifecycle stays
|
|
240
|
+
// in one translation unit (see GPUExternalTexture.cpp).
|
|
241
|
+
return GPUExternalTexture::Create(_instance, std::move(descriptor));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
std::shared_ptr<GPUSharedTextureMemory> GPUDevice::importSharedTextureMemory(
|
|
245
|
+
std::shared_ptr<GPUSharedTextureMemoryDescriptor> descriptor) {
|
|
246
|
+
if (!descriptor || descriptor->handle == nullptr) {
|
|
247
|
+
throw std::runtime_error("GPUDevice::importSharedTextureMemory(): handle "
|
|
248
|
+
"must be a non-null native pointer");
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
wgpu::SharedTextureMemoryDescriptor desc{};
|
|
252
|
+
std::string label = descriptor->label.value_or("");
|
|
253
|
+
if (!label.empty()) {
|
|
254
|
+
desc.label = wgpu::StringView(label.c_str(), label.size());
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
#if defined(__APPLE__)
|
|
258
|
+
wgpu::SharedTextureMemoryIOSurfaceDescriptor platformDesc{};
|
|
259
|
+
platformDesc.ioSurface = descriptor->handle;
|
|
260
|
+
// Default off: enabling it propagates StorageBinding into properties.usage,
|
|
261
|
+
// which then forces memory.createTexture() (no-descriptor form) to validate
|
|
262
|
+
// the format against storage capabilities. bgra8unorm (the standard
|
|
263
|
+
// CVPixelBuffer format) only supports storage when the device opts into the
|
|
264
|
+
// bgra8unorm-storage feature, so unconditionally setting this here breaks
|
|
265
|
+
// the common sample-only case.
|
|
266
|
+
platformDesc.allowStorageBinding = false;
|
|
267
|
+
desc.nextInChain = &platformDesc;
|
|
268
|
+
#elif defined(__ANDROID__)
|
|
269
|
+
wgpu::SharedTextureMemoryAHardwareBufferDescriptor platformDesc{};
|
|
270
|
+
platformDesc.handle = descriptor->handle;
|
|
271
|
+
desc.nextInChain = &platformDesc;
|
|
272
|
+
#else
|
|
237
273
|
throw std::runtime_error(
|
|
238
|
-
"GPUDevice::
|
|
274
|
+
"GPUDevice::importSharedTextureMemory(): unsupported platform");
|
|
275
|
+
#endif
|
|
276
|
+
|
|
277
|
+
auto memory = _instance.ImportSharedTextureMemory(&desc);
|
|
278
|
+
if (memory == nullptr) {
|
|
279
|
+
throw std::runtime_error("GPUDevice::importSharedTextureMemory(): "
|
|
280
|
+
"ImportSharedTextureMemory returned null - is the "
|
|
281
|
+
"'shared-texture-memory-iosurface' (Apple) or "
|
|
282
|
+
"'shared-texture-memory-ahardware-buffer' "
|
|
283
|
+
"(Android) feature enabled on the device?");
|
|
284
|
+
}
|
|
285
|
+
return std::make_shared<GPUSharedTextureMemory>(std::move(memory),
|
|
286
|
+
std::move(label));
|
|
239
287
|
}
|
|
240
288
|
|
|
241
289
|
async::AsyncTaskHandle GPUDevice::createComputePipelineAsync(
|
|
@@ -262,7 +310,7 @@ async::AsyncTaskHandle GPUDevice::createComputePipelineAsync(
|
|
|
262
310
|
&desc, wgpu::CallbackMode::AllowProcessEvents,
|
|
263
311
|
[pipelineHolder, resolve,
|
|
264
312
|
reject](wgpu::CreatePipelineAsyncStatus status,
|
|
265
|
-
wgpu::ComputePipeline pipeline,
|
|
313
|
+
wgpu::ComputePipeline pipeline, wgpu::StringView msg) {
|
|
266
314
|
if (status == wgpu::CreatePipelineAsyncStatus::Success && pipeline) {
|
|
267
315
|
pipelineHolder->_instance = pipeline;
|
|
268
316
|
resolve([pipelineHolder](jsi::Runtime &runtime) mutable {
|
|
@@ -271,7 +319,8 @@ async::AsyncTaskHandle GPUDevice::createComputePipelineAsync(
|
|
|
271
319
|
});
|
|
272
320
|
} else {
|
|
273
321
|
std::string error =
|
|
274
|
-
msg ? std::string(msg)
|
|
322
|
+
msg.length ? std::string(msg.data, msg.length)
|
|
323
|
+
: "Failed to create compute pipeline";
|
|
275
324
|
reject(std::move(error));
|
|
276
325
|
}
|
|
277
326
|
});
|
|
@@ -303,7 +352,7 @@ async::AsyncTaskHandle GPUDevice::createRenderPipelineAsync(
|
|
|
303
352
|
&desc, wgpu::CallbackMode::AllowProcessEvents,
|
|
304
353
|
[pipelineHolder, resolve,
|
|
305
354
|
reject](wgpu::CreatePipelineAsyncStatus status,
|
|
306
|
-
wgpu::RenderPipeline pipeline,
|
|
355
|
+
wgpu::RenderPipeline pipeline, wgpu::StringView msg) {
|
|
307
356
|
if (status == wgpu::CreatePipelineAsyncStatus::Success && pipeline) {
|
|
308
357
|
pipelineHolder->_instance = pipeline;
|
|
309
358
|
resolve([pipelineHolder](jsi::Runtime &runtime) mutable {
|
|
@@ -312,7 +361,8 @@ async::AsyncTaskHandle GPUDevice::createRenderPipelineAsync(
|
|
|
312
361
|
});
|
|
313
362
|
} else {
|
|
314
363
|
std::string error =
|
|
315
|
-
msg ? std::string(msg)
|
|
364
|
+
msg.length ? std::string(msg.data, msg.length)
|
|
365
|
+
: "Failed to create render pipeline";
|
|
316
366
|
reject(std::move(error));
|
|
317
367
|
}
|
|
318
368
|
});
|
|
@@ -386,12 +436,15 @@ std::unordered_set<std::string> GPUDevice::getFeatures() {
|
|
|
386
436
|
wgpu::SupportedFeatures supportedFeatures;
|
|
387
437
|
_instance.GetFeatures(&supportedFeatures);
|
|
388
438
|
std::unordered_set<std::string> result;
|
|
439
|
+
std::unordered_set<wgpu::FeatureName> enabled;
|
|
389
440
|
for (size_t i = 0; i < supportedFeatures.featureCount; ++i) {
|
|
390
441
|
auto feature = supportedFeatures.features[i];
|
|
442
|
+
enabled.insert(feature);
|
|
391
443
|
std::string name;
|
|
392
444
|
convertEnumToJSUnion(feature, &name);
|
|
393
445
|
result.insert(name);
|
|
394
446
|
}
|
|
447
|
+
maybeSynthesizeRnNativeTextureFeature(enabled, result);
|
|
395
448
|
return result;
|
|
396
449
|
}
|
|
397
450
|
|
|
@@ -45,6 +45,8 @@
|
|
|
45
45
|
#include "GPURenderPipelineDescriptor.h"
|
|
46
46
|
#include "GPUSampler.h"
|
|
47
47
|
#include "GPUSamplerDescriptor.h"
|
|
48
|
+
#include "GPUSharedTextureMemory.h"
|
|
49
|
+
#include "GPUSharedTextureMemoryDescriptor.h"
|
|
48
50
|
#include "GPUShaderModule.h"
|
|
49
51
|
#include "GPUShaderModuleDescriptor.h"
|
|
50
52
|
#include "GPUSupportedLimits.h"
|
|
@@ -116,6 +118,8 @@ public:
|
|
|
116
118
|
std::optional<std::shared_ptr<GPUSamplerDescriptor>> descriptor);
|
|
117
119
|
std::shared_ptr<GPUExternalTexture> importExternalTexture(
|
|
118
120
|
std::shared_ptr<GPUExternalTextureDescriptor> descriptor);
|
|
121
|
+
std::shared_ptr<GPUSharedTextureMemory> importSharedTextureMemory(
|
|
122
|
+
std::shared_ptr<GPUSharedTextureMemoryDescriptor> descriptor);
|
|
119
123
|
std::shared_ptr<GPUBindGroupLayout> createBindGroupLayout(
|
|
120
124
|
std::shared_ptr<GPUBindGroupLayoutDescriptor> descriptor);
|
|
121
125
|
std::shared_ptr<GPUPipelineLayout>
|
|
@@ -169,6 +173,8 @@ public:
|
|
|
169
173
|
&GPUDevice::createSampler);
|
|
170
174
|
installMethod(runtime, prototype, "importExternalTexture",
|
|
171
175
|
&GPUDevice::importExternalTexture);
|
|
176
|
+
installMethod(runtime, prototype, "importSharedTextureMemory",
|
|
177
|
+
&GPUDevice::importSharedTextureMemory);
|
|
172
178
|
installMethod(runtime, prototype, "createBindGroupLayout",
|
|
173
179
|
&GPUDevice::createBindGroupLayout);
|
|
174
180
|
installMethod(runtime, prototype, "createPipelineLayout",
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
#include "GPUExternalTexture.h"
|
|
2
|
+
|
|
3
|
+
#include <cmath>
|
|
4
|
+
#include <memory>
|
|
5
|
+
#include <string>
|
|
6
|
+
#include <utility>
|
|
7
|
+
|
|
8
|
+
#include "GPUExternalTextureDescriptor.h"
|
|
9
|
+
|
|
10
|
+
namespace rnwgpu {
|
|
11
|
+
|
|
12
|
+
// Identity gamut (BT.709 -> sRGB, same primaries) as a 3x3 column-major matrix.
|
|
13
|
+
static const float kIdentityGamutMatrix[9] = {
|
|
14
|
+
1.0f, 0.0f, 0.0f, //
|
|
15
|
+
0.0f, 1.0f, 0.0f, //
|
|
16
|
+
0.0f, 0.0f, 1.0f, //
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// Piecewise gamma transfer-function parameters Dawn expects:
|
|
20
|
+
// for |x| < D: y = sign(x) * (C * |x| + F)
|
|
21
|
+
// else : y = sign(x) * (pow(A * |x| + B, G) + E)
|
|
22
|
+
// sRGB decode (encoded -> linear).
|
|
23
|
+
static const float kSrgbDecodeParams[7] = {
|
|
24
|
+
2.4f, // G
|
|
25
|
+
1.0f / 1.055f, // A
|
|
26
|
+
0.055f / 1.055f, // B
|
|
27
|
+
1.0f / 12.92f, // C
|
|
28
|
+
0.04045f, // D
|
|
29
|
+
0.0f, // E
|
|
30
|
+
0.0f, // F
|
|
31
|
+
};
|
|
32
|
+
// sRGB encode (linear -> encoded).
|
|
33
|
+
static const float kSrgbEncodeParams[7] = {
|
|
34
|
+
1.0f / 2.4f, // G
|
|
35
|
+
1.055f, // A
|
|
36
|
+
0.0f, // B
|
|
37
|
+
12.92f, // C
|
|
38
|
+
0.0031308f, // D
|
|
39
|
+
-0.055f, // E
|
|
40
|
+
0.0f, // F
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Identity transfer (y = x). Used when the sampled surface is already in the
|
|
44
|
+
// render target's color space: a single-plane BGRA IOSurface, or the Android
|
|
45
|
+
// opaque-YCbCr path where the Vulkan sampler already produced RGB. Dawn
|
|
46
|
+
// dereferences the transfer-function arrays unconditionally
|
|
47
|
+
// (ComputeExternalTextureParams), so these must be non-null even when no
|
|
48
|
+
// conversion is wanted.
|
|
49
|
+
static const float kIdentityTransferParams[7] = {
|
|
50
|
+
1.0f, // G
|
|
51
|
+
1.0f, // A
|
|
52
|
+
0.0f, // B
|
|
53
|
+
0.0f, // C
|
|
54
|
+
0.0f, // D
|
|
55
|
+
0.0f, // E
|
|
56
|
+
0.0f, // F
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// BT.709 limited-range YUV -> R'G'B' as a 3x4 row-major matrix mapping
|
|
60
|
+
// [Y, Cb, Cr, 1] to gamma-encoded R'G'B' (NOT linear; the sRGB decode in
|
|
61
|
+
// srcTransferFunctionParameters linearizes afterwards). Same values the Apple
|
|
62
|
+
// NV12 path computes from the CVPixelBuffer; used for Android buffers that
|
|
63
|
+
// arrive as a *defined* biplanar format (where we split the planes and convert
|
|
64
|
+
// ourselves) rather than an opaque external-format AHB. Camera streams are
|
|
65
|
+
// limited-range BT.709 in the overwhelming majority of cases; full-range /
|
|
66
|
+
// BT.601 would need different coefficients (refine from the buffer's suggested
|
|
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, //
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// True for the multi-planar Y + CbCr formats whose planes we can view as
|
|
75
|
+
// Plane0Only (luma) / Plane1Only (chroma) and convert with an explicit matrix.
|
|
76
|
+
// Excludes OpaqueYCbCrAndroid (external format, no plane views) and triplanar
|
|
77
|
+
// formats (would need a third plane). Only referenced on Android.
|
|
78
|
+
[[maybe_unused]] static bool isBiplanarYuvFormat(wgpu::TextureFormat format) {
|
|
79
|
+
switch (format) {
|
|
80
|
+
case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
|
|
81
|
+
case wgpu::TextureFormat::R8BG8Biplanar422Unorm:
|
|
82
|
+
case wgpu::TextureFormat::R8BG8Biplanar444Unorm:
|
|
83
|
+
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
|
|
84
|
+
case wgpu::TextureFormat::R10X6BG10X6Biplanar422Unorm:
|
|
85
|
+
case wgpu::TextureFormat::R10X6BG10X6Biplanar444Unorm:
|
|
86
|
+
return true;
|
|
87
|
+
default:
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Map a rotation in degrees (0 / 90 / 180 / 270) to Dawn's enum. Anything that
|
|
93
|
+
// isn't a clean multiple of 90 snaps to the nearest quadrant; Dawn only
|
|
94
|
+
// supports those four steps for external textures.
|
|
95
|
+
static wgpu::ExternalTextureRotation
|
|
96
|
+
toExternalTextureRotation(double degrees) {
|
|
97
|
+
int quadrant = static_cast<int>(std::lround(degrees / 90.0)) & 3;
|
|
98
|
+
switch (quadrant) {
|
|
99
|
+
case 1:
|
|
100
|
+
return wgpu::ExternalTextureRotation::Rotate90Degrees;
|
|
101
|
+
case 2:
|
|
102
|
+
return wgpu::ExternalTextureRotation::Rotate180Degrees;
|
|
103
|
+
case 3:
|
|
104
|
+
return wgpu::ExternalTextureRotation::Rotate270Degrees;
|
|
105
|
+
default:
|
|
106
|
+
return wgpu::ExternalTextureRotation::Rotate0Degrees;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
std::shared_ptr<GPUExternalTexture> GPUExternalTexture::Create(
|
|
111
|
+
wgpu::Device device,
|
|
112
|
+
std::shared_ptr<GPUExternalTextureDescriptor> descriptor) {
|
|
113
|
+
if (!descriptor || !descriptor->source) {
|
|
114
|
+
throw std::runtime_error(
|
|
115
|
+
"GPUExternalTexture::Create(): descriptor.source (VideoFrame) "
|
|
116
|
+
"is required");
|
|
117
|
+
}
|
|
118
|
+
const auto &source = descriptor->source;
|
|
119
|
+
const auto &frame = source->handle();
|
|
120
|
+
if (frame.handle == nullptr) {
|
|
121
|
+
throw std::runtime_error(
|
|
122
|
+
"GPUExternalTexture::Create(): VideoFrame has been released");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
#if defined(__APPLE__)
|
|
126
|
+
// 1. Import the IOSurface as SharedTextureMemory. For NV12 surfaces this
|
|
127
|
+
// yields a biplanar texture; for BGRA, a single-plane one.
|
|
128
|
+
wgpu::SharedTextureMemoryDescriptor memDesc{};
|
|
129
|
+
std::string label = descriptor->label.value_or("external-texture");
|
|
130
|
+
if (!label.empty()) {
|
|
131
|
+
memDesc.label = wgpu::StringView(label.c_str(), label.size());
|
|
132
|
+
}
|
|
133
|
+
wgpu::SharedTextureMemoryIOSurfaceDescriptor platformDesc{};
|
|
134
|
+
platformDesc.ioSurface = frame.handle;
|
|
135
|
+
// ExternalTexture views are sampled-only; storage binding isn't needed and
|
|
136
|
+
// for biplanar formats it would fail validation.
|
|
137
|
+
platformDesc.allowStorageBinding = false;
|
|
138
|
+
memDesc.nextInChain = &platformDesc;
|
|
139
|
+
auto memory = device.ImportSharedTextureMemory(&memDesc);
|
|
140
|
+
if (memory == nullptr) {
|
|
141
|
+
throw std::runtime_error(
|
|
142
|
+
"GPUExternalTexture::Create(): ImportSharedTextureMemory "
|
|
143
|
+
"returned null. Is 'shared-texture-memory-iosurface' enabled?");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// 2. Create the texture from the surface. We pass the right format
|
|
147
|
+
// explicitly so Dawn picks the multi-planar variant on NV12.
|
|
148
|
+
bool isYuv = frame.pixelFormat == VideoPixelFormat::NV12;
|
|
149
|
+
auto texture = memory.CreateTexture();
|
|
150
|
+
if (texture == nullptr) {
|
|
151
|
+
throw std::runtime_error(
|
|
152
|
+
"GPUExternalTexture::Create(): CreateTexture returned null");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 3. Begin access on the underlying memory. The matching EndAccess runs when
|
|
156
|
+
// the GPUExternalTexture is destroyed (explicitly via destroy() or at GC).
|
|
157
|
+
wgpu::SharedTextureMemoryBeginAccessDescriptor begin{};
|
|
158
|
+
begin.initialized = true;
|
|
159
|
+
begin.concurrentRead = false;
|
|
160
|
+
if (!memory.BeginAccess(texture, &begin)) {
|
|
161
|
+
throw std::runtime_error(
|
|
162
|
+
"GPUExternalTexture::Create(): BeginAccess failed");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// 4. Build plane views. For NV12 we need plane0 = R8 luma and plane1 = RG8
|
|
166
|
+
// chroma; for BGRA we only set plane0.
|
|
167
|
+
wgpu::TextureView plane0;
|
|
168
|
+
wgpu::TextureView plane1;
|
|
169
|
+
{
|
|
170
|
+
wgpu::TextureViewDescriptor v{};
|
|
171
|
+
v.aspect =
|
|
172
|
+
isYuv ? wgpu::TextureAspect::Plane0Only : wgpu::TextureAspect::All;
|
|
173
|
+
plane0 = texture.CreateView(&v);
|
|
174
|
+
}
|
|
175
|
+
if (isYuv) {
|
|
176
|
+
wgpu::TextureViewDescriptor v{};
|
|
177
|
+
v.aspect = wgpu::TextureAspect::Plane1Only;
|
|
178
|
+
plane1 = texture.CreateView(&v);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// 5. Build the ExternalTextureDescriptor. We hand Dawn explicit YUV→RGB and
|
|
182
|
+
// sRGB transfer-function parameters so the sampler does the full color
|
|
183
|
+
// conversion in hardware.
|
|
184
|
+
wgpu::ExternalTextureDescriptor extDesc{};
|
|
185
|
+
if (!label.empty()) {
|
|
186
|
+
extDesc.label = wgpu::StringView(label.c_str(), label.size());
|
|
187
|
+
}
|
|
188
|
+
extDesc.plane0 = plane0;
|
|
189
|
+
extDesc.gamutConversionMatrix = kIdentityGamutMatrix;
|
|
190
|
+
if (isYuv) {
|
|
191
|
+
extDesc.plane1 = plane1;
|
|
192
|
+
extDesc.yuvToRgbConversionMatrix = frame.yuvToRgbMatrix;
|
|
193
|
+
extDesc.srcTransferFunctionParameters = kSrgbDecodeParams;
|
|
194
|
+
extDesc.dstTransferFunctionParameters = kSrgbEncodeParams;
|
|
195
|
+
} else {
|
|
196
|
+
// BGRA is already RGB in the target color space; pass it through. Dawn
|
|
197
|
+
// dereferences these arrays unconditionally, so they must be non-null.
|
|
198
|
+
extDesc.srcTransferFunctionParameters = kIdentityTransferParams;
|
|
199
|
+
extDesc.dstTransferFunctionParameters = kIdentityTransferParams;
|
|
200
|
+
}
|
|
201
|
+
extDesc.cropOrigin = {0, 0};
|
|
202
|
+
extDesc.cropSize = {frame.width, frame.height};
|
|
203
|
+
extDesc.apparentSize = {frame.width, frame.height};
|
|
204
|
+
extDesc.mirrored = descriptor->mirrored.value_or(false);
|
|
205
|
+
extDesc.rotation =
|
|
206
|
+
toExternalTextureRotation(descriptor->rotation.value_or(0));
|
|
207
|
+
|
|
208
|
+
auto external = device.CreateExternalTexture(&extDesc);
|
|
209
|
+
if (external == nullptr) {
|
|
210
|
+
wgpu::SharedTextureMemoryEndAccessState state{};
|
|
211
|
+
(void)memory.EndAccess(texture, &state);
|
|
212
|
+
throw std::runtime_error(
|
|
213
|
+
"GPUExternalTexture::Create(): CreateExternalTexture returned "
|
|
214
|
+
"null");
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return std::make_shared<GPUExternalTexture>(
|
|
218
|
+
std::move(external), std::move(memory), std::move(texture),
|
|
219
|
+
std::move(descriptor->source), std::move(label));
|
|
220
|
+
#elif defined(__ANDROID__)
|
|
221
|
+
// 1. Import the AHardwareBuffer as SharedTextureMemory. For YUV AHBs this
|
|
222
|
+
// yields a Dawn texture in the implementation-defined OpaqueYCbCrAndroid
|
|
223
|
+
// format; for RGBA AHBs, a regular single-plane texture.
|
|
224
|
+
wgpu::SharedTextureMemoryDescriptor memDesc{};
|
|
225
|
+
std::string label = descriptor->label.value_or("external-texture");
|
|
226
|
+
if (!label.empty()) {
|
|
227
|
+
memDesc.label = wgpu::StringView(label.c_str(), label.size());
|
|
228
|
+
}
|
|
229
|
+
wgpu::SharedTextureMemoryAHardwareBufferDescriptor platformDesc{};
|
|
230
|
+
platformDesc.handle = frame.handle;
|
|
231
|
+
memDesc.nextInChain = &platformDesc;
|
|
232
|
+
auto memory = device.ImportSharedTextureMemory(&memDesc);
|
|
233
|
+
if (memory == nullptr) {
|
|
234
|
+
throw std::runtime_error(
|
|
235
|
+
"GPUExternalTexture::Create(): ImportSharedTextureMemory "
|
|
236
|
+
"returned null. Is 'shared-texture-memory-ahardware-buffer' enabled?");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// 2. Create the texture. No descriptor: Dawn picks the right format
|
|
240
|
+
// (OpaqueYCbCrAndroid for YUV, R8 / RGBA8 / ... for color AHBs).
|
|
241
|
+
auto texture = memory.CreateTexture();
|
|
242
|
+
if (texture == nullptr) {
|
|
243
|
+
throw std::runtime_error(
|
|
244
|
+
"GPUExternalTexture::Create(): CreateTexture returned null");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// 3. Begin access. Vulkan requires us to advertise the incoming VkImage
|
|
248
|
+
// layout (UNDEFINED is fine for the first acquisition of an AHB whose
|
|
249
|
+
// contents we expect Dawn to read as-is).
|
|
250
|
+
wgpu::SharedTextureMemoryBeginAccessDescriptor begin{};
|
|
251
|
+
begin.initialized = true;
|
|
252
|
+
begin.concurrentRead = false;
|
|
253
|
+
wgpu::SharedTextureMemoryVkImageLayoutBeginState beginLayout{};
|
|
254
|
+
begin.nextInChain = &beginLayout;
|
|
255
|
+
if (!memory.BeginAccess(texture, &begin)) {
|
|
256
|
+
throw std::runtime_error(
|
|
257
|
+
"GPUExternalTexture::Create(): BeginAccess failed");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// 4. Build the ExternalTextureDescriptor. There are two cases depending on
|
|
261
|
+
// how Dawn imported the AHB (see SharedTextureMemoryVk.cpp):
|
|
262
|
+
//
|
|
263
|
+
// a. *External* format (camera buffers whose layout has no Vulkan
|
|
264
|
+
// equivalent) -> OpaqueYCbCrAndroid, a single opaque plane. Sampling
|
|
265
|
+
// routes through a Vulkan SamplerYcbcrConversion whose model Dawn
|
|
266
|
+
// copies verbatim from the AHB's suggestedYcbcrModel. We pass a single
|
|
267
|
+
// plane + identity transfer and let that conversion (if any) run. NOTE:
|
|
268
|
+
// when the driver reports RGB_IDENTITY the sample comes back as raw
|
|
269
|
+
// Y/Cb/Cr; there is no public hook to override the model on this path.
|
|
270
|
+
//
|
|
271
|
+
// b. *Defined* biplanar format (e.g. R8BG8Biplanar420Unorm, exposed by the
|
|
272
|
+
// dawn-multi-planar-formats feature) -> we split Plane0Only (luma) /
|
|
273
|
+
// Plane1Only (chroma) and hand Dawn an explicit BT.709 matrix + sRGB
|
|
274
|
+
// transfer, exactly like the iOS NV12 path. This makes numPlanes == 2
|
|
275
|
+
// so the matrix is actually applied (the single-plane branch in Dawn's
|
|
276
|
+
// Tint transform ignores yuvToRgbConversionMatrix).
|
|
277
|
+
//
|
|
278
|
+
// Either way we must pass non-null gamut/transfer arrays:
|
|
279
|
+
// ComputeExternalTextureParams dereferences them unconditionally
|
|
280
|
+
// (kIdentityTransferParams is defined at file scope).
|
|
281
|
+
const bool isBiplanar = frame.pixelFormat == VideoPixelFormat::NV12 &&
|
|
282
|
+
isBiplanarYuvFormat(texture.GetFormat());
|
|
283
|
+
|
|
284
|
+
wgpu::TextureView plane0;
|
|
285
|
+
wgpu::TextureView plane1;
|
|
286
|
+
wgpu::ExternalTextureDescriptor extDesc{};
|
|
287
|
+
if (!label.empty()) {
|
|
288
|
+
extDesc.label = wgpu::StringView(label.c_str(), label.size());
|
|
289
|
+
}
|
|
290
|
+
extDesc.cropOrigin = {0, 0};
|
|
291
|
+
extDesc.cropSize = {frame.width, frame.height};
|
|
292
|
+
extDesc.apparentSize = {frame.width, frame.height};
|
|
293
|
+
extDesc.gamutConversionMatrix = kIdentityGamutMatrix;
|
|
294
|
+
if (isBiplanar) {
|
|
295
|
+
wgpu::TextureViewDescriptor v0{};
|
|
296
|
+
v0.aspect = wgpu::TextureAspect::Plane0Only;
|
|
297
|
+
plane0 = texture.CreateView(&v0);
|
|
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
|
+
}
|
|
312
|
+
extDesc.mirrored = descriptor->mirrored.value_or(false);
|
|
313
|
+
extDesc.rotation =
|
|
314
|
+
toExternalTextureRotation(descriptor->rotation.value_or(0));
|
|
315
|
+
|
|
316
|
+
auto external = device.CreateExternalTexture(&extDesc);
|
|
317
|
+
if (external == nullptr) {
|
|
318
|
+
wgpu::SharedTextureMemoryEndAccessState state{};
|
|
319
|
+
(void)memory.EndAccess(texture, &state);
|
|
320
|
+
throw std::runtime_error(
|
|
321
|
+
"GPUExternalTexture::Create(): CreateExternalTexture returned "
|
|
322
|
+
"null");
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return std::make_shared<GPUExternalTexture>(
|
|
326
|
+
std::move(external), std::move(memory), std::move(texture),
|
|
327
|
+
std::move(descriptor->source), std::move(label));
|
|
328
|
+
#else
|
|
329
|
+
throw std::runtime_error(
|
|
330
|
+
"GPUExternalTexture::Create(): not yet implemented on this "
|
|
331
|
+
"platform");
|
|
332
|
+
#endif
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
} // namespace rnwgpu
|