diff --git a/filament/backend/CMakeLists.txt b/filament/backend/CMakeLists.txt index c01a54cf1b..e9bf42a5df 100644 --- a/filament/backend/CMakeLists.txt +++ b/filament/backend/CMakeLists.txt @@ -104,6 +104,7 @@ if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3) list(APPEND SRCS src/VirtualMachineEnv.cpp) endif () if (ANDROID) + list(APPEND SRCS src/AndroidNdk.cpp) list(APPEND SRCS src/AndroidNativeWindow.cpp) list(APPEND SRCS src/AndroidSwapChainHelper.cpp) list(APPEND SRCS src/AndroidFrameCallback.cpp) diff --git a/filament/backend/include/backend/platforms/PlatformEGLAndroid.h b/filament/backend/include/backend/platforms/PlatformEGLAndroid.h index 67b5bd82b7..977814dba9 100644 --- a/filament/backend/include/backend/platforms/PlatformEGLAndroid.h +++ b/filament/backend/include/backend/platforms/PlatformEGLAndroid.h @@ -19,6 +19,7 @@ #include "AndroidSwapChainHelper.h" #include "AndroidFrameCallback.h" +#include "AndroidNdk.h" #include #include @@ -46,7 +47,7 @@ class ExternalStreamManagerAndroid; * A concrete implementation of OpenGLPlatform and subclass of PlatformEGL that supports * EGL on Android. It adds Android streaming functionality to PlatformEGL. */ -class PlatformEGLAndroid : public PlatformEGL { +class PlatformEGLAndroid : public PlatformEGL, public AndroidNdk { public: PlatformEGLAndroid() noexcept; diff --git a/filament/backend/include/backend/platforms/VulkanPlatformAndroid.h b/filament/backend/include/backend/platforms/VulkanPlatformAndroid.h index 9f217874c4..179a7505c8 100644 --- a/filament/backend/include/backend/platforms/VulkanPlatformAndroid.h +++ b/filament/backend/include/backend/platforms/VulkanPlatformAndroid.h @@ -18,6 +18,7 @@ #define TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H #include "AndroidFrameCallback.h" +#include "AndroidNdk.h" #include #include @@ -26,7 +27,7 @@ namespace filament::backend { -class VulkanPlatformAndroid : public VulkanPlatform { +class VulkanPlatformAndroid : public VulkanPlatform, public AndroidNdk { public: ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer, bool sRGB) noexcept; diff --git a/filament/backend/src/AndroidNativeWindow.cpp b/filament/backend/src/AndroidNativeWindow.cpp index 95bcf3773c..af476fb402 100644 --- a/filament/backend/src/AndroidNativeWindow.cpp +++ b/filament/backend/src/AndroidNativeWindow.cpp @@ -16,6 +16,7 @@ #include "AndroidNativeWindow.h" +#include #include #include @@ -30,11 +31,16 @@ namespace filament::backend { std::pair NativeWindow::isValid(ANativeWindow* const anw) noexcept { +#if __ANDROID_API__ >= 26 + // libnativewindow.so is not available before API level 26, this means we can't call + // any method above 25 (even protected by __builtin_available()). if (__builtin_available(android 28, *)) { // this a proxy for is_valid() auto const result = ANativeWindow_getBuffersDataSpace(anw); return { result, result >= 0 }; } +#endif + // fallback on using private APIs NativeWindow const* pWindow = reinterpret_cast(anw); if (UTILS_LIKELY(pWindow->query)) { @@ -82,6 +88,10 @@ AndroidProducerThrottling::AndroidProducerThrottling() { // note: we don't need to dlclose() mNativeWindowLib here, because the library will be cleaned // when the process ends and dlopen() are ref-counted. dlclose() NDK documentation documents // not to call dlclose(). + + // libnativewindow.so is not available before API level 26, this means we can't call + // any method above 25 (even protected by __builtin_available()). + void* nativeWindowLibHandle = dlopen("libnativewindow.so", RTLD_LOCAL | RTLD_NOW); if (nativeWindowLibHandle) { mSetProducerThrottlingEnabled = diff --git a/filament/backend/src/AndroidNdk.cpp b/filament/backend/src/AndroidNdk.cpp new file mode 100644 index 0000000000..441df22020 --- /dev/null +++ b/filament/backend/src/AndroidNdk.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AndroidNdk.h" + +#include + +#include + +#include + +#include + +namespace filament::backend { + +UTILS_UNUSED +static std::once_flag sInitOnce{}; + +template +UTILS_UNUSED +static void loadSymbol(void* handle, const char* symbol, T& pfn) { + pfn = T(dlsym(handle, symbol)); +} + +#if FILAMENT_USE_DLSYM(26) +AndroidNdk::Ndk AndroidNdk::ndk{}; +#endif + +AndroidNdk::AndroidNdk() { +#if FILAMENT_USE_DLSYM(26) + std::call_once(sInitOnce, [] { + // note: we don't need to dlclose() mNativeWindowLib here, because the library will be cleaned + // when the process ends and dlopen() are ref-counted. dlclose() NDK documentation documents + // not to call dlclose(). + if (__builtin_available(android 26, *)) { + void* h = dlopen("libnativewindow.so", RTLD_LOCAL | RTLD_NOW); + if (h) { + loadSymbol(h, "AHardwareBuffer_acquire", ndk.AHardwareBuffer_acquire); + loadSymbol(h, "AHardwareBuffer_release", ndk.AHardwareBuffer_release); + loadSymbol(h, "AHardwareBuffer_describe", ndk.AHardwareBuffer_describe); + } + } + }); +#endif +} + +} // filament::backend diff --git a/filament/backend/src/AndroidNdk.h b/filament/backend/src/AndroidNdk.h new file mode 100644 index 0000000000..a37667108c --- /dev/null +++ b/filament/backend/src/AndroidNdk.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FILAMENT_BACKEND_ANDROIDNDK_H +#define FILAMENT_BACKEND_ANDROIDNDK_H + +#include +#include + +#define FILAMENT_REQUIRES_API(x) __attribute__((__availability__(android,introduced=x))) + +#define FILAMENT_USE_DLSYM(api) (__ANDROID_API__ < (api)) + +namespace filament::backend { + +class AndroidNdk { +public: + AndroidNdk(); + +#if FILAMENT_USE_DLSYM(26) + + static void AHardwareBuffer_acquire( + AHardwareBuffer* buffer) FILAMENT_REQUIRES_API(26) { + ndk.AHardwareBuffer_acquire(buffer); + } + + static void AHardwareBuffer_release( + AHardwareBuffer* buffer) FILAMENT_REQUIRES_API(26) { + ndk.AHardwareBuffer_release(buffer); + } + + static void AHardwareBuffer_describe( + AHardwareBuffer const* buffer, AHardwareBuffer_Desc* desc) FILAMENT_REQUIRES_API(26) { + ndk.AHardwareBuffer_describe(buffer, desc); + } + +#else + + static void AHardwareBuffer_acquire( + AHardwareBuffer* buffer) FILAMENT_REQUIRES_API(26) { + ::AHardwareBuffer_acquire(buffer); + } + static void AHardwareBuffer_release( + AHardwareBuffer* buffer) FILAMENT_REQUIRES_API(26) { + ::AHardwareBuffer_release(buffer); + } + static void AHardwareBuffer_describe( + AHardwareBuffer const* buffer, AHardwareBuffer_Desc* desc) FILAMENT_REQUIRES_API(26) { + ::AHardwareBuffer_describe(buffer, desc); + } + +#endif + +private: +#if FILAMENT_USE_DLSYM(26) + static struct Ndk { + void (*AHardwareBuffer_acquire)(AHardwareBuffer*); + void (*AHardwareBuffer_release)(AHardwareBuffer*); + void (*AHardwareBuffer_describe)(AHardwareBuffer const*, AHardwareBuffer_Desc*); + } ndk; +#endif +}; + +} // filament::backend + +#endif //FILAMENT_BACKEND_ANDROIDNDK_H