Skip to content

Commit 139d50c

Browse files
authored
fix(🍏): fix instanced draw calls on iOS simulator (#415)
1 parent 62e76f5 commit 139d50c

13 files changed

Lines changed: 707 additions & 16 deletions

File tree

β€Ž.github/workflows/ci.ymlβ€Ž

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,17 @@ jobs:
137137
working-directory: packages/webgpu
138138
run: yarn test
139139

140+
# Upload the snapshot folder so reference images generated on this run
141+
# (checkImage writes missing snapshots) and the *.test.png /
142+
# *-diff-test.png images from failed comparisons can be inspected.
143+
# `always()` so the artifact is available precisely when tests fail.
144+
- name: Upload test snapshots
145+
if: always()
146+
uses: actions/upload-artifact@v4
147+
with:
148+
name: webgpu-test-snapshots
149+
path: packages/webgpu/src/__tests__/snapshots/
150+
140151
- name: Detect ANDROID_HOME
141152
run: |
142153
if [ -n "$ANDROID_HOME" ] && [ -d "$ANDROID_HOME" ]; then
@@ -182,7 +193,7 @@ jobs:
182193
working-directory: apps/example/android
183194
env:
184195
JAVA_OPTS: "-XX:MaxHeapSize=6g"
185-
run: ./gradlew assembleDebug --build-cache --warning-mode all
196+
run: ./gradlew assembleDebug --build-cache --warning-mode all --stacktrace
186197

187198
# Housekeeping for the self-hosted runner: the start-of-run kill already
188199
# guarantees a clean slate for the next build, so this is not needed for

β€Žapps/example/src/useClient.tsβ€Ž

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,36 @@ export const useClient = (): UseClient => {
1414
useEffect(() => {
1515
const url = `ws://${HOST}:${PORT}`;
1616
let it: ReturnType<typeof setTimeout>;
17+
let disposed = false;
1718
const ws = new WebSocket(url);
19+
const scheduleRetry = () => {
20+
if (disposed) {
21+
return;
22+
}
23+
it = setTimeout(() => {
24+
// incrementing retry to rerun the effect
25+
setRetry((r) => r + 1);
26+
}, 500);
27+
};
1828
ws.onopen = () => {
1929
setClient(ws);
2030
ws.send(JSON.stringify({ OS: Platform.OS, arch: "paper" }));
2131
};
32+
// Reconnect on every close, not only on error: the test server closes the
33+
// socket cleanly at the end of each jest run, and without a retry here the
34+
// app would need a manual reload before the next run.
2235
ws.onclose = () => {
2336
setClient(null);
37+
scheduleRetry();
2438
};
2539
ws.onerror = () => {
26-
it = setTimeout(() => {
27-
ws.close();
28-
// incrementing retry to rerun the effect
29-
setRetry((r) => r + 1);
30-
}, 500);
40+
// Triggers onclose, which schedules the retry.
41+
ws.close();
3142
};
3243
return () => {
33-
ws.close();
44+
disposed = true;
3445
clearTimeout(it);
46+
ws.close();
3547
};
3648
}, [retry]);
3749
return [client, HOST];

β€Žpackages/webgpu/cpp/rnwgpu/api/GPUAdapter.cppβ€Ž

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@
22

33
#include <algorithm>
44
#include <cstdio>
5+
#include <cstring>
56
#include <memory>
67
#include <string>
78
#include <unordered_set>
89
#include <utility>
910
#include <vector>
1011

12+
#ifdef __APPLE__
13+
#include <TargetConditionals.h>
14+
#endif
15+
1116
#include "Convertors.h"
1217

1318
#include "GPUFeatures.h"
@@ -150,16 +155,39 @@ async::AsyncTaskHandle GPUAdapter::requestDevice(
150155
for (const auto &t : dawnToggles->enabledToggles.value()) {
151156
enabledToggles.push_back(t.c_str());
152157
}
153-
toggles.enabledToggleCount = enabledToggles.size();
154-
toggles.enabledToggles = enabledToggles.data();
155158
}
156159
if (dawnToggles->disabledToggles) {
157160
for (const auto &t : dawnToggles->disabledToggles.value()) {
158161
disabledToggles.push_back(t.c_str());
159162
}
160-
toggles.disabledToggleCount = disabledToggles.size();
161-
toggles.disabledToggles = disabledToggles.data();
162163
}
164+
}
165+
#if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR
166+
// The iOS Simulator only advertises MTLFeatureSet_iOS_GPUFamily2, so
167+
// Dawn defaults disable_base_instance/disable_base_vertex on and then
168+
// rejects every draw with a non-zero firstInstance or baseVertex,
169+
// both core WebGPU. The simulator forwards Metal calls to the host
170+
// GPU, which does support base vertex/instance drawing, so force the
171+
// toggles off. Device builds are unaffected: WebGPU-capable iPhones
172+
// and iPads are all GPUFamily3+. These are device-stage toggles, so
173+
// they must be chained here rather than on the instance descriptor
174+
// (instance-stage toggle parsing silently drops them).
175+
static const char *const kSimulatorDisabledToggles[] = {
176+
"disable_base_instance", "disable_base_vertex"};
177+
for (const char *name : kSimulatorDisabledToggles) {
178+
const bool explicitlyEnabled = std::any_of(
179+
enabledToggles.begin(), enabledToggles.end(),
180+
[name](const char *t) { return std::strcmp(t, name) == 0; });
181+
if (!explicitlyEnabled) {
182+
disabledToggles.push_back(name);
183+
}
184+
}
185+
#endif
186+
if (!enabledToggles.empty() || !disabledToggles.empty()) {
187+
toggles.enabledToggleCount = enabledToggles.size();
188+
toggles.enabledToggles = enabledToggles.data();
189+
toggles.disabledToggleCount = disabledToggles.size();
190+
toggles.disabledToggles = disabledToggles.data();
163191
deviceDesc.nextInChain = &toggles;
164192
}
165193
_instance.RequestDevice(

β€Žpackages/webgpu/package.jsonβ€Ž

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"scripts": {
2727
"test": "NODE_OPTIONS='--experimental-require-module' jest -i",
2828
"test:ref": "REFERENCE=true NODE_OPTIONS='--experimental-require-module' jest -i",
29+
"test:node": "NODE_WEBGPU=true NODE_OPTIONS='--experimental-require-module' jest -i",
2930
"test:plugin": "jest -c plugin/jest.config.js",
3031
"lint": "eslint . --ext .ts,.tsx --max-warnings 0 --cache --fix",
3132
"tsc": "tsc --noEmit",
@@ -107,6 +108,7 @@
107108
"ts-morph": "^22.0.0",
108109
"tsx": "^4.20.5",
109110
"typescript": "^5.2.2",
111+
"webgpu": "0.4.0",
110112
"wgpu-matrix": "^3.0.2",
111113
"ws": "^8.18.0",
112114
"yargs": "^17.7.2"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export const DEBUG = process.env.DEBUG === "true";
22
export const REFERENCE = process.env.REFERENCE === "true";
3+
export const NODE_WEBGPU = process.env.NODE_WEBGPU === "true";

0 commit comments

Comments
Β (0)