Skip to content

Commit 3eff735

Browse files
authored
getNativeSurface() + lazy WebGPU context intialization (#59)
1 parent 9aa408b commit 3eff735

24 files changed

+367
-49
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#pragma once
2+
3+
#include <memory>
4+
5+
#include "webgpu/webgpu_cpp.h"
6+
7+
#include "PlatformContext.h"
8+
#include "RNWebGPUManager.h"
9+
10+
namespace rnwgpu {
11+
class AndroidPlatformContext : public PlatformContext {
12+
public:
13+
AndroidPlatformContext() = default;
14+
~AndroidPlatformContext() = default;
15+
16+
wgpu::Surface makeSurface(wgpu::Instance instance, void *window, int width,
17+
int height) override {
18+
wgpu::SurfaceDescriptorFromAndroidNativeWindow androidSurfaceDesc;
19+
androidSurfaceDesc.window = reinterpret_cast<ANativeWindow *>(window);
20+
wgpu::SurfaceDescriptor surfaceDescriptor;
21+
surfaceDescriptor.nextInChain = &androidSurfaceDesc;
22+
return instance.CreateSurface(&surfaceDescriptor);
23+
}
24+
};
25+
26+
} // namespace rnwgpu

package/android/cpp/cpp-adapter.cpp

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,20 @@
88
#include <android/native_window_jni.h>
99
#include <webgpu/webgpu_cpp.h>
1010

11+
#include "AndroidPlatformContext.h"
1112
#include "GPUCanvasContext.h"
1213
#include "RNWebGPUManager.h"
1314

1415
#define LOG_TAG "WebGPUModule"
1516

1617
std::shared_ptr<rnwgpu::RNWebGPUManager> manager;
17-
std::unordered_map<int, ANativeWindow *> windowsRegistry;
1818

1919
extern "C" JNIEXPORT void JNICALL Java_com_webgpu_WebGPUModule_initializeNative(
2020
JNIEnv *env, jobject /* this */, jlong jsRuntime, jobject jsInvokerHolder) {
2121
auto runtime = reinterpret_cast<facebook::jsi::Runtime *>(jsRuntime);
22-
manager = std::make_shared<rnwgpu::RNWebGPUManager>(runtime, nullptr);
22+
auto platformContext = std::make_shared<rnwgpu::AndroidPlatformContext>();
23+
manager = std::make_shared<rnwgpu::RNWebGPUManager>(runtime, nullptr,
24+
platformContext);
2325
}
2426

2527
extern "C" JNIEXPORT void JNICALL
@@ -39,33 +41,29 @@ Java_com_webgpu_WebGPUModule_createSurfaceContext(JNIEnv *env, jobject thiz,
3941
// Context already exists
4042
return;
4143
}
42-
4344
auto label = "Context: " + std::to_string(contextId);
44-
auto gpuCanvasContext =
45-
std::make_shared<rnwgpu::GPUCanvasContext>(*surfaceData);
46-
auto gpuCanvasContextJs =
47-
facebook::jsi::Object::createFromHostObject(*runtime, gpuCanvasContext);
45+
auto resultObject = facebook::jsi::Object(*runtime);
46+
resultObject.setProperty(*runtime, "width", surfaceData->width);
47+
resultObject.setProperty(*runtime, "height", surfaceData->height);
48+
auto surfaceBigInt = facebook::jsi::BigInt::fromUint64(
49+
*runtime, reinterpret_cast<uint64_t>(surfaceData->surface));
50+
resultObject.setProperty(*runtime, "surface", surfaceBigInt);
4851
webGPUContextRegistry.setProperty(*runtime, std::to_string(contextId).c_str(),
49-
gpuCanvasContextJs);
52+
resultObject);
5053
}
5154

5255
extern "C" JNIEXPORT void JNICALL Java_com_webgpu_WebGPUView_onSurfaceCreate(
5356
JNIEnv *env, jobject thiz, jobject surface, jint contextId, jfloat width,
5457
jfloat height) {
5558
auto window = ANativeWindow_fromSurface(env, surface);
56-
windowsRegistry[contextId] = window;
57-
wgpu::SurfaceDescriptorFromAndroidNativeWindow androidSurfaceDesc;
58-
androidSurfaceDesc.window = window;
59-
wgpu::SurfaceDescriptor surfaceDescriptor;
60-
surfaceDescriptor.nextInChain = &androidSurfaceDesc;
61-
auto surfaceGpu = std::make_shared<wgpu::Surface>(
62-
manager->getGPU()->get().CreateSurface(&surfaceDescriptor));
63-
rnwgpu::SurfaceData surfaceData = {width, height, surfaceGpu};
59+
// ANativeWindow_acquire(window);
60+
rnwgpu::SurfaceData surfaceData = {width, height, window};
6461
manager->surfacesRegistry.addSurface(contextId, surfaceData);
6562
}
6663

6764
extern "C" JNIEXPORT void JNICALL Java_com_webgpu_WebGPUView_onSurfaceDestroy(
6865
JNIEnv *env, jobject thiz, jint contextId) {
69-
ANativeWindow_release(windowsRegistry[contextId]);
66+
auto surfaceData = manager->surfacesRegistry.getSurface(contextId);
67+
ANativeWindow_release(static_cast<ANativeWindow *>(surfaceData->surface));
7068
manager->surfacesRegistry.removeSurface(contextId);
7169
}

package/cpp/jsi/RNFJSIConverter.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,12 @@ template <> struct JSIConverter<std::nullptr_t> {
9090
template <typename T>
9191
struct JSIConverter<T, std::enable_if_t<!std::is_same_v<T, uint64_t> && std::is_same_v<T, size_t>>> {
9292
static size_t fromJSI(jsi::Runtime& runtime, const jsi::Value& arg, bool outOfBound) {
93-
double value = arg.asNumber();
94-
return static_cast<size_t>(value);
93+
if (arg.isNumber()) {
94+
double value = arg.asNumber();
95+
return static_cast<size_t>(value);
96+
} else {
97+
return arg.asBigInt(runtime).asInt64(runtime);
98+
}
9599
}
96100

97101
static jsi::Value toJSI(jsi::Runtime& runtime, size_t arg) {
@@ -112,12 +116,16 @@ template <> struct JSIConverter<uint32_t> {
112116

113117
// uint64_t <> BigInt
114118
template <> struct JSIConverter<uint64_t> {
115-
static double fromJSI(jsi::Runtime& runtime, const jsi::Value& arg, bool outOfBound) {
119+
static uint64_t fromJSI(jsi::Runtime& runtime, const jsi::Value& arg, bool outOfBound) {
120+
if (arg.isNumber()) {
116121
double value = arg.asNumber();
117122
if (value < 0 || value > static_cast<double>(std::numeric_limits<uint64_t>::max())) {
118123
throw jsi::JSError(runtime, "Number out of range for uint64_t");
119124
}
120125
return static_cast<uint64_t>(value);
126+
} else {
127+
return arg.asBigInt(runtime).getUint64(runtime);
128+
}
121129
}
122130

123131
static jsi::Value toJSI(jsi::Runtime& runtime, uint64_t arg) {

package/cpp/rnwgpu/PlatformContext.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#pragma once
2+
3+
#include "webgpu/webgpu_cpp.h"
4+
5+
namespace rnwgpu {
6+
7+
class PlatformContext {
8+
public:
9+
PlatformContext() = default;
10+
virtual ~PlatformContext() = default;
11+
12+
virtual wgpu::Surface makeSurface(wgpu::Instance instance, void *surface,
13+
int width, int height) = 0;
14+
};
15+
16+
} // namespace rnwgpu

package/cpp/rnwgpu/RNWebGPUManager.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ namespace rnwgpu {
1717

1818
RNWebGPUManager::RNWebGPUManager(
1919
jsi::Runtime *jsRuntime,
20-
std::shared_ptr<facebook::react::CallInvoker> jsCallInvoker)
21-
: _jsRuntime(jsRuntime), _jsCallInvoker(jsCallInvoker) {
20+
std::shared_ptr<facebook::react::CallInvoker> jsCallInvoker,
21+
std::shared_ptr<PlatformContext> platformContext)
22+
: _jsRuntime(jsRuntime), _jsCallInvoker(jsCallInvoker),
23+
_platformContext(platformContext) {
2224

2325
_gpu = std::make_shared<GPU>();
24-
auto navigator = std::make_shared<Navigator>(_gpu);
26+
auto navigator = std::make_shared<Navigator>(_gpu, _platformContext);
2527

2628
auto bufferUsage = std::make_shared<GPUBufferUsage>();
2729
_jsRuntime->global().setProperty(

package/cpp/rnwgpu/RNWebGPUManager.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <memory>
44

55
#include "GPU.h"
6+
#include "PlatformContext.h"
67
#include "SurfaceRegistry.h"
78

89
namespace facebook {
@@ -22,7 +23,8 @@ namespace react = facebook::react;
2223
class RNWebGPUManager {
2324
public:
2425
RNWebGPUManager(jsi::Runtime *jsRuntime,
25-
std::shared_ptr<facebook::react::CallInvoker> jsCallInvoker);
26+
std::shared_ptr<facebook::react::CallInvoker> jsCallInvoker,
27+
std::shared_ptr<PlatformContext> platformContext);
2628
~RNWebGPUManager();
2729

2830
SurfaceRegistry surfacesRegistry;
@@ -32,6 +34,7 @@ class RNWebGPUManager {
3234
private:
3335
jsi::Runtime *_jsRuntime;
3436
std::shared_ptr<facebook::react::CallInvoker> _jsCallInvoker;
37+
std::shared_ptr<PlatformContext> _platformContext;
3538
std::shared_ptr<GPU> _gpu;
3639
};
3740

package/cpp/rnwgpu/SurfaceRegistry.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace rnwgpu {
1010
struct SurfaceData {
1111
float width = 0;
1212
float height = 0;
13-
std::shared_ptr<wgpu::Surface> surface;
13+
void *surface;
1414
};
1515

1616
class SurfaceRegistry {

package/cpp/rnwgpu/api/GPUCanvasContext.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ std::shared_ptr<GPUTexture> GPUCanvasContext::getCurrentTexture() {
3434
wgpu::SurfaceTexture surfaceTexture;
3535
_instance.GetCurrentTexture(&surfaceTexture);
3636
auto texture = surfaceTexture.texture;
37+
if (texture == nullptr) {
38+
throw std::runtime_error("Couldn't get current texture");
39+
}
3740
// Default canvas texture label is ""
3841
return std::make_shared<GPUTexture>(texture, "");
3942
}

package/cpp/rnwgpu/api/GPUCanvasContext.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,9 @@ class Canvas : public m::HybridObject {
5959

6060
class GPUCanvasContext : public m::HybridObject {
6161
public:
62-
explicit GPUCanvasContext(const SurfaceData &surfaceData)
63-
: HybridObject("GPUCanvasContext"), _instance(*surfaceData.surface),
64-
_canvas(std::make_shared<rnwgpu::Canvas>(surfaceData.width,
65-
surfaceData.height)) {}
62+
explicit GPUCanvasContext(wgpu::Surface instance, float width, float height)
63+
: HybridObject("GPUCanvasContext"), _instance(instance),
64+
_canvas(std::make_shared<rnwgpu::Canvas>(width, height)) {}
6665

6766
public:
6867
std::string getBrand() { return _name; }

package/cpp/rnwgpu/api/Navigator.h

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,41 @@
33
#include <memory>
44

55
#include "GPU.h"
6+
#include "GPUCanvasContext.h"
67

78
namespace rnwgpu {
89

910
namespace m = margelo;
1011

1112
class Navigator : public m::HybridObject {
1213
public:
13-
explicit Navigator(std::shared_ptr<GPU> gpu)
14-
: HybridObject("Navigator"), _gpu(gpu) {}
14+
explicit Navigator(std::shared_ptr<GPU> gpu,
15+
std::shared_ptr<PlatformContext> platformContext)
16+
: HybridObject("Navigator"), _gpu(gpu),
17+
_platformContext(platformContext) {}
1518

1619
std::shared_ptr<GPU> getGPU() { return _gpu; }
1720

21+
std::shared_ptr<GPUCanvasContext>
22+
MakeWebGPUCanvasContext(uint64_t nativeSurface, float width, float height) {
23+
auto surface = _platformContext->makeSurface(
24+
_gpu->get(), reinterpret_cast<void *>(nativeSurface), width, height);
25+
if (surface == nullptr) {
26+
throw std::runtime_error("null surface");
27+
}
28+
auto ctx = std::make_shared<GPUCanvasContext>(surface, width, height);
29+
return ctx;
30+
}
31+
1832
void loadHybridMethods() override {
1933
registerHybridGetter("gpu", &Navigator::getGPU, this);
34+
registerHybridMethod("MakeWebGPUCanvasContext",
35+
&Navigator::MakeWebGPUCanvasContext, this);
2036
}
2137

2238
private:
2339
std::shared_ptr<GPU> _gpu;
40+
std::shared_ptr<PlatformContext> _platformContext;
2441
};
2542

26-
} // namespace rnwgpu
43+
} // namespace rnwgpu
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#pragma once
2+
3+
#include <memory>
4+
#include <string>
5+
#include <utility>
6+
7+
#include "Unions.h"
8+
9+
#include "webgpu/webgpu_cpp.h"
10+
11+
#include "RNFHybridObject.h"
12+
13+
#include "AsyncRunner.h"
14+
15+
#include "GPUCanvasContext.h"
16+
17+
#ifdef __APPLE__
18+
#include "WebGPUModule.h"
19+
#endif
20+
21+
namespace rnwgpu {
22+
23+
namespace m = margelo;
24+
25+
class WebGPUCanvasContextFactory : public m::HybridObject {
26+
public:
27+
GPUCanvasContextFactory() : HybridObject("GPUCanvasContextFactory") {}
28+
29+
public:
30+
std::shared_ptr<GPUCanvasContext> Make(uint64_t surface, int width,
31+
int height) {
32+
#ifdef __APPLE__
33+
wgpu::SurfaceDescriptorFromMetalLayer metalSurfaceDesc;
34+
metalSurfaceDesc.layer = reinterpret_cast<void *>(surface);
35+
36+
wgpu::SurfaceDescriptor surfaceDescriptor;
37+
surfaceDescriptor.nextInChain = &metalSurfaceDesc;
38+
39+
auto surfaceGpu = std::make_unique<wgpu::Surface>(
40+
WebGPUModule::getManager()->getGPU()->get().CreateSurface(
41+
&surfaceDescriptor));
42+
const float scaleFactor = 1;
43+
const float scaledWidth = width * scaleFactor;
44+
const float scaledHeight = height * scaleFactor;
45+
46+
rnwgpu::SurfaceData surfaceData{scaledWidth, scaledHeight,
47+
std::move(surfaceGpu)};
48+
#elif __ANDROID__
49+
throw std::runtime_error("Not implemented");
50+
#endif
51+
return std::make_shared<rnwgpu::GPUCanvasContext>(surfaceData);
52+
}
53+
54+
void loadHybridMethods() override {
55+
registerHybridGetter("Make", &GPUCanvasContextFactory::Make, this);
56+
}
57+
};
58+
59+
} // namespace rnwgpu

package/example/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { ABuffer } from "./ABuffer";
1313
import { OcclusionQuery } from "./OcclusionQuery";
1414
import { ComputeBoids } from "./ComputeBoids";
1515
import { Wireframe } from "./Wireframe";
16+
import { Resize } from "./Resize";
1617

1718
const Stack = createNativeStackNavigator<Routes>();
1819

@@ -43,6 +44,7 @@ function App() {
4344
<Stack.Screen name="OcclusionQuery" component={OcclusionQuery} />
4445
<Stack.Screen name="ComputeBoids" component={ComputeBoids} />
4546
<Stack.Screen name="Wireframe" component={Wireframe} />
47+
<Stack.Screen name="Resize" component={Resize} />
4648
<Stack.Screen name="Tests">
4749
{(props) => <Tests {...props} assets={assets} />}
4850
</Stack.Screen>

package/example/src/Home.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ import type { NativeStackNavigationProp } from "@react-navigation/native-stack";
77
import type { Routes } from "./Route";
88

99
export const examples = [
10+
{
11+
screen: "Tests",
12+
title: "🧪 E2E Tests",
13+
},
1014
{
1115
screen: "HelloTriangle",
1216
title: "🔺 Hello Triangle",
@@ -48,8 +52,8 @@ export const examples = [
4852
title: "🧬 Wireframe",
4953
},
5054
{
51-
screen: "Tests",
52-
title: "🧪 E2E Tests",
55+
screen: "Resize",
56+
title: "↔️ Resize",
5357
},
5458
] as const;
5559

0 commit comments

Comments
 (0)