diff --git a/package/cpp/core/RNFEngineImpl.cpp b/package/cpp/core/RNFEngineImpl.cpp index b82b0ac5..4bf33896 100644 --- a/package/cpp/core/RNFEngineImpl.cpp +++ b/package/cpp/core/RNFEngineImpl.cpp @@ -4,6 +4,7 @@ #include "RNFEngineImpl.h" +#include "RNFEngineBackendEnum.h" #include "RNFReferences.h" #include "utils/RNFConverter.h" @@ -392,4 +393,18 @@ void EngineImpl::flushAndWait() { _engine->flushAndWait(); } +std::string EngineImpl::getBackend() { + std::string result; + EnumMapper::convertEnumToJSUnion(_engine->getBackend(), &result); + return result; +} + +int EngineImpl::getSupportedFeatureLevel() { + return static_cast(_engine->getSupportedFeatureLevel()); +} + +int EngineImpl::getActiveFeatureLevel() { + return static_cast(_engine->getActiveFeatureLevel()); +} + } // namespace margelo diff --git a/package/cpp/core/RNFEngineImpl.h b/package/cpp/core/RNFEngineImpl.h index 743a0c6d..d6456205 100644 --- a/package/cpp/core/RNFEngineImpl.h +++ b/package/cpp/core/RNFEngineImpl.h @@ -69,6 +69,11 @@ class EngineImpl : public std::enable_shared_from_this { void flushAndWait(); + // Engine capability queries — thin wrappers around filament::Engine const getters. + std::string getBackend(); + int getSupportedFeatureLevel(); + int getActiveFeatureLevel(); + private: std::mutex _mutex; std::shared_ptr _engine; diff --git a/package/cpp/core/RNFEngineWrapper.cpp b/package/cpp/core/RNFEngineWrapper.cpp index 5960f930..e2fadc42 100644 --- a/package/cpp/core/RNFEngineWrapper.cpp +++ b/package/cpp/core/RNFEngineWrapper.cpp @@ -54,6 +54,9 @@ void EngineWrapper::loadHybridMethods() { registerHybridMethod("clearSkybox", &EngineWrapper::clearSkybox, this); registerHybridMethod("setAutomaticInstancingEnabled", &EngineWrapper::setAutomaticInstancingEnabled, this); registerHybridMethod("flushAndWait", &EngineWrapper::flushAndWait, this); + registerHybridMethod("getBackend", &EngineWrapper::getBackend, this); + registerHybridMethod("getSupportedFeatureLevel", &EngineWrapper::getSupportedFeatureLevel, this); + registerHybridMethod("getActiveFeatureLevel", &EngineWrapper::getActiveFeatureLevel, this); } void EngineWrapper::setSurfaceProvider(std::shared_ptr surfaceProvider) { pointee()->setSurfaceProvider(surfaceProvider); @@ -196,5 +199,14 @@ void EngineWrapper::setAutomaticInstancingEnabled(bool enabled) { void EngineWrapper::flushAndWait() { pointee()->flushAndWait(); } +std::string EngineWrapper::getBackend() { + return pointee()->getBackend(); +} +int EngineWrapper::getSupportedFeatureLevel() { + return pointee()->getSupportedFeatureLevel(); +} +int EngineWrapper::getActiveFeatureLevel() { + return pointee()->getActiveFeatureLevel(); +} } // namespace margelo diff --git a/package/cpp/core/RNFEngineWrapper.h b/package/cpp/core/RNFEngineWrapper.h index a3302a11..c9a94466 100644 --- a/package/cpp/core/RNFEngineWrapper.h +++ b/package/cpp/core/RNFEngineWrapper.h @@ -84,6 +84,9 @@ class EngineWrapper : public PointerHolder { void clearSkybox(); void setAutomaticInstancingEnabled(bool enabled); void flushAndWait(); + std::string getBackend(); + int getSupportedFeatureLevel(); + int getActiveFeatureLevel(); private: static constexpr auto TAG = "EngineWrapper"; diff --git a/package/docs/engine-info.md b/package/docs/engine-info.md new file mode 100644 index 00000000..f3a141f4 --- /dev/null +++ b/package/docs/engine-info.md @@ -0,0 +1,82 @@ +# Engine Backend & Feature Level Info + +`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. + +## API + +### `engine.getBackend()` + +Returns the resolved graphics backend as a string: + +| Value | Meaning | +|-------|---------| +| `"metal"` | Apple Metal (default on iOS/macOS) | +| `"opengl"` | OpenGL ES (default on Android) | +| `"vulkan"` | Vulkan (opt-in on Android/Linux/Windows) | +| `"default"` | Should not appear at runtime -- the engine always resolves to a concrete backend | + +### `engine.getSupportedFeatureLevel()` + +Returns the highest feature level the device's GPU supports, as an integer `0`-`3`: + +| Level | Capabilities | +|-------|-------------| +| `0` | OpenGL ES 2.0 features only | +| `1` | OpenGL ES 3.0 features (default on most modern devices) | +| `2` | OpenGL ES 3.1 + 16 texture units + cubemap arrays | +| `3` | OpenGL ES 3.1 + 31 texture units + cubemap arrays | + +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`. + +### `engine.getActiveFeatureLevel()` + +Returns the currently active feature level. This equals the supported level unless a lower level was explicitly set via `Engine.Builder.featureLevel()`. + +## Example: Adaptive Quality + +```tsx +import { useFilamentContext } from 'react-native-filament'; +import { useEffect } from 'react'; + +function AdaptiveScene() { + const { engine } = useFilamentContext(); + + useEffect(() => { + const backend = engine.getBackend(); + const featureLevel = engine.getSupportedFeatureLevel(); + + console.log(`Backend: ${backend}, Feature Level: ${featureLevel}`); + + // Use feature level as a GPU capability signal: + // Level 0-1: budget GPU -- use lower-poly models, fewer draw calls + // Level 2+: capable GPU -- enable full-quality rendering + }, [engine]); + + return ( + // ... your scene + ); +} +``` + +## Example: Device Diagnostics Screen + +```tsx +import { useFilamentContext } from 'react-native-filament'; +import { Text, View } from 'react-native'; + +function DiagnosticsPanel() { + const { engine } = useFilamentContext(); + + return ( + + Backend: {engine.getBackend()} + Supported Feature Level: {engine.getSupportedFeatureLevel()} + Active Feature Level: {engine.getActiveFeatureLevel()} + + ); +} +``` + +## Background + +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. diff --git a/package/src/types/Engine.ts b/package/src/types/Engine.ts index 05bf297d..1d352add 100644 --- a/package/src/types/Engine.ts +++ b/package/src/types/Engine.ts @@ -142,4 +142,53 @@ export interface Engine extends PointerHolder { * Note: during on screen rendering this is handled automatically, typically used for offscreen rendering (recording). */ flushAndWait(): void + + /** + * Returns the resolved graphics backend as a string. + * + * On iOS this is typically `"metal"`, on Android `"opengl"` (or `"vulkan"` if explicitly + * selected). This returns the **actual** backend in use, even if `"default"` was requested + * at engine creation time. + * + * Useful for telemetry, diagnostics, and adaptive rendering decisions. + * + * @example + * ```ts + * const { engine } = useFilamentContext(); + * console.log(engine.getBackend()); // "opengl" on most Android devices + * ``` + */ + getBackend(): 'opengl' | 'vulkan' | 'metal' | 'default' + + /** + * Returns the highest feature level supported by the device's GPU. + * + * Feature levels map to OpenGL ES capabilities: + * - `0` — OpenGL ES 2.0 features only + * - `1` — OpenGL ES 3.0 features (default on most modern devices) + * - `2` — OpenGL ES 3.1 + 16 texture units + cubemap arrays + * - `3` — OpenGL ES 3.1 + 31 texture units + cubemap arrays + * + * This is a useful GPU capability signal for adaptive quality decisions. A device + * supporting feature level 2+ has a meaningfully more capable GPU than one at level 0-1. + * + * @example + * ```ts + * const { engine } = useFilamentContext(); + * const level = engine.getSupportedFeatureLevel(); + * if (level >= 2) { + * // Enable higher-quality rendering options + * } + * ``` + */ + getSupportedFeatureLevel(): 0 | 1 | 2 | 3 + + /** + * Returns the currently active feature level. + * + * This may differ from {@link getSupportedFeatureLevel} if a lower level was explicitly + * set via `Engine.Builder.featureLevel()`. By default the active level equals the + * supported level. + */ + getActiveFeatureLevel(): 0 | 1 | 2 | 3 }