Skip to content

Commit 35288a0

Browse files
author
Andy Cross
committed
feat: expose Engine backend and feature level info to JavaScript
Bridge three existing C++ Engine getters through the JSI layer: - getBackend(): returns the resolved graphics backend ("opengl", "vulkan", "metal") - getSupportedFeatureLevel(): returns the highest GPU feature level (0-3) - getActiveFeatureLevel(): returns the currently active feature level These are zero-cost const getters that already exist on filament::Engine but were not accessible from JavaScript. They enable adaptive quality decisions, device profiling, and telemetry without requiring additional native modules or GL context creation. See docs/engine-info.md for usage examples. Made-with: Cursor
1 parent 957de31 commit 35288a0

6 files changed

Lines changed: 166 additions & 0 deletions

File tree

package/cpp/core/RNFEngineImpl.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "RNFEngineImpl.h"
66

7+
#include "RNFEngineBackendEnum.h"
78
#include "RNFReferences.h"
89
#include "utils/RNFConverter.h"
910

@@ -392,4 +393,18 @@ void EngineImpl::flushAndWait() {
392393
_engine->flushAndWait();
393394
}
394395

396+
std::string EngineImpl::getBackend() {
397+
std::string result;
398+
EnumMapper::convertEnumToJSUnion(_engine->getBackend(), &result);
399+
return result;
400+
}
401+
402+
int EngineImpl::getSupportedFeatureLevel() {
403+
return static_cast<int>(_engine->getSupportedFeatureLevel());
404+
}
405+
406+
int EngineImpl::getActiveFeatureLevel() {
407+
return static_cast<int>(_engine->getActiveFeatureLevel());
408+
}
409+
395410
} // namespace margelo

package/cpp/core/RNFEngineImpl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ class EngineImpl : public std::enable_shared_from_this<EngineImpl> {
6969

7070
void flushAndWait();
7171

72+
// Engine capability queries — thin wrappers around filament::Engine const getters.
73+
std::string getBackend();
74+
int getSupportedFeatureLevel();
75+
int getActiveFeatureLevel();
76+
7277
private:
7378
std::mutex _mutex;
7479
std::shared_ptr<Engine> _engine;

package/cpp/core/RNFEngineWrapper.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ void EngineWrapper::loadHybridMethods() {
5454
registerHybridMethod("clearSkybox", &EngineWrapper::clearSkybox, this);
5555
registerHybridMethod("setAutomaticInstancingEnabled", &EngineWrapper::setAutomaticInstancingEnabled, this);
5656
registerHybridMethod("flushAndWait", &EngineWrapper::flushAndWait, this);
57+
registerHybridMethod("getBackend", &EngineWrapper::getBackend, this);
58+
registerHybridMethod("getSupportedFeatureLevel", &EngineWrapper::getSupportedFeatureLevel, this);
59+
registerHybridMethod("getActiveFeatureLevel", &EngineWrapper::getActiveFeatureLevel, this);
5760
}
5861
void EngineWrapper::setSurfaceProvider(std::shared_ptr<SurfaceProvider> surfaceProvider) {
5962
pointee()->setSurfaceProvider(surfaceProvider);
@@ -196,5 +199,14 @@ void EngineWrapper::setAutomaticInstancingEnabled(bool enabled) {
196199
void EngineWrapper::flushAndWait() {
197200
pointee()->flushAndWait();
198201
}
202+
std::string EngineWrapper::getBackend() {
203+
return pointee()->getBackend();
204+
}
205+
int EngineWrapper::getSupportedFeatureLevel() {
206+
return pointee()->getSupportedFeatureLevel();
207+
}
208+
int EngineWrapper::getActiveFeatureLevel() {
209+
return pointee()->getActiveFeatureLevel();
210+
}
199211

200212
} // namespace margelo

package/cpp/core/RNFEngineWrapper.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ class EngineWrapper : public PointerHolder<EngineImpl> {
8484
void clearSkybox();
8585
void setAutomaticInstancingEnabled(bool enabled);
8686
void flushAndWait();
87+
std::string getBackend();
88+
int getSupportedFeatureLevel();
89+
int getActiveFeatureLevel();
8790

8891
private:
8992
static constexpr auto TAG = "EngineWrapper";

package/docs/engine-info.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Engine Backend & Feature Level Info
2+
3+
`react-native-filament` exposes three read-only getters from the underlying Filament engine that describe the GPU backend and its capabilities. These are useful for adaptive quality, diagnostics, and telemetry.
4+
5+
## API
6+
7+
### `engine.getBackend()`
8+
9+
Returns the resolved graphics backend as a string:
10+
11+
| Value | Meaning |
12+
|-------|---------|
13+
| `"metal"` | Apple Metal (default on iOS/macOS) |
14+
| `"opengl"` | OpenGL ES (default on Android) |
15+
| `"vulkan"` | Vulkan (opt-in on Android/Linux/Windows) |
16+
| `"default"` | Should not appear at runtime -- the engine always resolves to a concrete backend |
17+
18+
### `engine.getSupportedFeatureLevel()`
19+
20+
Returns the highest feature level the device's GPU supports, as an integer `0`-`3`:
21+
22+
| Level | Capabilities |
23+
|-------|-------------|
24+
| `0` | OpenGL ES 2.0 features only |
25+
| `1` | OpenGL ES 3.0 features (default on most modern devices) |
26+
| `2` | OpenGL ES 3.1 + 16 texture units + cubemap arrays |
27+
| `3` | OpenGL ES 3.1 + 31 texture units + cubemap arrays |
28+
29+
On iOS with Metal, devices typically report feature level `3`. On Android the level depends on the GPU -- budget GPUs may report `1`, while mid-range and flagship GPUs report `2` or `3`.
30+
31+
### `engine.getActiveFeatureLevel()`
32+
33+
Returns the currently active feature level. This equals the supported level unless a lower level was explicitly set via `Engine.Builder.featureLevel()`.
34+
35+
## Example: Adaptive Quality
36+
37+
```tsx
38+
import { useFilamentContext } from 'react-native-filament';
39+
import { useEffect } from 'react';
40+
41+
function AdaptiveScene() {
42+
const { engine } = useFilamentContext();
43+
44+
useEffect(() => {
45+
const backend = engine.getBackend();
46+
const featureLevel = engine.getSupportedFeatureLevel();
47+
48+
console.log(`Backend: ${backend}, Feature Level: ${featureLevel}`);
49+
50+
// Use feature level as a GPU capability signal:
51+
// Level 0-1: budget GPU -- use lower-poly models, fewer draw calls
52+
// Level 2+: capable GPU -- enable full-quality rendering
53+
}, [engine]);
54+
55+
return (
56+
// ... your scene
57+
);
58+
}
59+
```
60+
61+
## Example: Device Diagnostics Screen
62+
63+
```tsx
64+
import { useFilamentContext } from 'react-native-filament';
65+
import { Text, View } from 'react-native';
66+
67+
function DiagnosticsPanel() {
68+
const { engine } = useFilamentContext();
69+
70+
return (
71+
<View>
72+
<Text>Backend: {engine.getBackend()}</Text>
73+
<Text>Supported Feature Level: {engine.getSupportedFeatureLevel()}</Text>
74+
<Text>Active Feature Level: {engine.getActiveFeatureLevel()}</Text>
75+
</View>
76+
);
77+
}
78+
```
79+
80+
## Background
81+
82+
Filament's C++ `Engine` class has always had `getBackend()`, `getSupportedFeatureLevel()`, and `getActiveFeatureLevel()` -- zero-cost const getters that return immediately. This change bridges them through the JSI layer so they are accessible from JavaScript, following the same `EngineImpl` -> `EngineWrapper` -> TypeScript pattern used by all other Engine methods.

package/src/types/Engine.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,4 +142,53 @@ export interface Engine extends PointerHolder {
142142
* Note: during on screen rendering this is handled automatically, typically used for offscreen rendering (recording).
143143
*/
144144
flushAndWait(): void
145+
146+
/**
147+
* Returns the resolved graphics backend as a string.
148+
*
149+
* On iOS this is typically `"metal"`, on Android `"opengl"` (or `"vulkan"` if explicitly
150+
* selected). This returns the **actual** backend in use, even if `"default"` was requested
151+
* at engine creation time.
152+
*
153+
* Useful for telemetry, diagnostics, and adaptive rendering decisions.
154+
*
155+
* @example
156+
* ```ts
157+
* const { engine } = useFilamentContext();
158+
* console.log(engine.getBackend()); // "opengl" on most Android devices
159+
* ```
160+
*/
161+
getBackend(): 'opengl' | 'vulkan' | 'metal' | 'default'
162+
163+
/**
164+
* Returns the highest feature level supported by the device's GPU.
165+
*
166+
* Feature levels map to OpenGL ES capabilities:
167+
* - `0` — OpenGL ES 2.0 features only
168+
* - `1` — OpenGL ES 3.0 features (default on most modern devices)
169+
* - `2` — OpenGL ES 3.1 + 16 texture units + cubemap arrays
170+
* - `3` — OpenGL ES 3.1 + 31 texture units + cubemap arrays
171+
*
172+
* This is a useful GPU capability signal for adaptive quality decisions. A device
173+
* supporting feature level 2+ has a meaningfully more capable GPU than one at level 0-1.
174+
*
175+
* @example
176+
* ```ts
177+
* const { engine } = useFilamentContext();
178+
* const level = engine.getSupportedFeatureLevel();
179+
* if (level >= 2) {
180+
* // Enable higher-quality rendering options
181+
* }
182+
* ```
183+
*/
184+
getSupportedFeatureLevel(): 0 | 1 | 2 | 3
185+
186+
/**
187+
* Returns the currently active feature level.
188+
*
189+
* This may differ from {@link getSupportedFeatureLevel} if a lower level was explicitly
190+
* set via `Engine.Builder.featureLevel()`. By default the active level equals the
191+
* supported level.
192+
*/
193+
getActiveFeatureLevel(): 0 | 1 | 2 | 3
145194
}

0 commit comments

Comments
 (0)