diff --git a/.gitlab-ci/android-skips.txt b/.gitlab-ci/android-skips.txt index 21e9e2ff6d8..9a77ee22fef 100644 --- a/.gitlab-ci/android-skips.txt +++ b/.gitlab-ci/android-skips.txt @@ -1,8 +1,4 @@ -# Skip these tests when running fractional dEQP batches, as the AHB tests are expected -# to be handled separately in a non-fractional run within the deqp-runner suite. +# Skip these tests when running fractional dEQP batches, as the AHB and WSI tests are +# expected to be handled separately in non-fractional runs within the deqp-runner suite. dEQP-VK.api.external.memory.android_hardware_buffer.* - -# Skip all WSI tests: the DEQP_ANDROID_EXE build used can't create native windows, as -# only APKs support window creation on Android. -dEQP-VK.image.swapchain_mutable.* dEQP-VK.wsi.* diff --git a/.gitlab-ci/container/build-deqp.sh b/.gitlab-ci/container/build-deqp.sh index 76e2867df44..82d363adc3e 100755 --- a/.gitlab-ci/container/build-deqp.sh +++ b/.gitlab-ci/container/build-deqp.sh @@ -52,6 +52,7 @@ vk_cts_commits_to_backport=( # shellcheck disable=SC2034 vk_cts_patch_files=( + build-deqp-android-Implement-headless-WSI-fallback-using-AImageR.patch ) # shellcheck disable=SC2034 @@ -60,6 +61,7 @@ gl_cts_commits_to_backport=( # shellcheck disable=SC2034 gl_cts_patch_files=( + build-deqp-android-Implement-headless-WSI-fallback-using-AImageR.patch # We're only applying this avoid conflicts in the second patch build-deqp-gl_Build-Don-t-build-Vulkan-utilities-for-GL-builds.patch ) @@ -74,6 +76,7 @@ gles_cts_commits_to_backport=( # shellcheck disable=SC2034 gles_cts_patch_files=( + build-deqp-android-Implement-headless-WSI-fallback-using-AImageR.patch build-deqp-gl_Build-Don-t-build-Vulkan-utilities-for-GL-builds.patch ) diff --git a/.gitlab-ci/container/patches/build-deqp-android-Implement-headless-WSI-fallback-using-AImageR.patch b/.gitlab-ci/container/patches/build-deqp-android-Implement-headless-WSI-fallback-using-AImageR.patch new file mode 100644 index 00000000000..7abc7d83089 --- /dev/null +++ b/.gitlab-ci/container/patches/build-deqp-android-Implement-headless-WSI-fallback-using-AImageR.patch @@ -0,0 +1,277 @@ +From 0ce7ca82289ec3351ccec4b37540d8fdbc8dd095 Mon Sep 17 00:00:00 2001 +From: Valentine Burley +Date: Thu, 23 Apr 2026 11:03:14 +0200 +Subject: [PATCH] android: Implement headless WSI fallback using AImageReader + +When running dEQP as a native executable (DEQP_ANDROID_EXE) on Android, +a system-provided NativeActivity window is typically unavailable. This +prevents Vulkan and EGL WSI tests from running as they require a valid +ANativeWindow. + +This patch: +- Implements a headless fallback using the AImageReader NDK API to + create an off-screen ANativeWindow in tcuAndroidPlatform.cpp. +- Adds ImageReaderNativeWindow (EGL) and ImageReaderVulkanWindow (Vulkan) + to manage the AImageReader lifecycle within the framework. +- Extracts the AImageReader creation logic into a shared helper + acquireImageReaderWindow(). +- Links against mediandk in android.cmake to provide AImageReader + support for Android API levels >= 24. +- On Android API levels < 24 (where AImageReader is unavailable), window + acquisition failure now consistently throws ResourceError instead of + NotSupportedError. This aligns the EGL path with the existing Vulkan + behavior and reverts EGL to the previous CTS behavior. + +This allows surface and swapchain tests to function correctly in headless +environments for both EGL and Vulkan. + +Components: Android, EGL, Framework, Vulkan +VK-GL-CTS issue: 6468 +Affects: +dEQP-EGL.* +dEQP-VK.wsi.android.* + +Change-Id: I462e617ae60e4dc3d9f0aeec11fd1628d0c6ff12 +Signed-off-by: Valentine Burley +--- + external/openglcts/README.md | 7 +- + .../platform/android/tcuAndroidPlatform.cpp | 159 +++++++++++++++++- + targets/android/android.cmake | 5 + + 3 files changed, 164 insertions(+), 7 deletions(-) + +diff --git a/external/openglcts/README.md b/external/openglcts/README.md +index 69eb8a5c1e..f464291858 100644 +--- a/external/openglcts/README.md ++++ b/external/openglcts/README.md +@@ -323,9 +323,10 @@ This is identical to the builds on other platforms and is better for iterative + runs of headless tests as CTS can be invoked and the output can be checked from + a single interactive terminal. + +-This build doesn't support WSI tests and shouldn't be used for conformance +-submissions, it also isn't recommended for longer running tests since Android +-will terminate this process as soon as the `adb shell` session ends which may ++This build supports WSI tests via a headless AImageReader fallback for both EGL ++and Vulkan (Android API 24+). However, it shouldn't be used for conformance ++submissions. It also isn't recommended for longer running tests since Android ++will terminate this process as soon as the `adb shell` session ends, which may + happen due to an unintentional device disconnection. + + cmake -GNinja -DCMAKE_BUILD_TYPE=Debug \ +diff --git a/framework/platform/android/tcuAndroidPlatform.cpp b/framework/platform/android/tcuAndroidPlatform.cpp +index af56dabb83..86984202fb 100644 +--- a/framework/platform/android/tcuAndroidPlatform.cpp ++++ b/framework/platform/android/tcuAndroidPlatform.cpp +@@ -35,6 +35,12 @@ + + // Assume no call translation is needed + #include ++#if DE_ANDROID_API >= 24 ++#include ++#endif ++#if DE_ANDROID_API >= 26 ++#include ++#endif + struct egl_native_pixmap_t; + DE_STATIC_ASSERT(sizeof(eglw::EGLNativeDisplayType) == sizeof(void *)); + DE_STATIC_ASSERT(sizeof(eglw::EGLNativePixmapType) == sizeof(struct egl_native_pixmap_t *)); +@@ -136,6 +142,81 @@ private: + WindowRegistry &m_windowRegistry; + }; + ++#if DE_ANDROID_API >= 24 ++static ANativeWindow *acquireImageReaderWindow(int width, int height, int32_t format, AImageReader **outReader) ++{ ++ AImageReader *reader = nullptr; ++#if DE_ANDROID_API >= 26 ++ uint64_t usage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY; ++ media_status_t status = AImageReader_newWithUsage(width, height, format, usage, 2, &reader); ++#else ++ media_status_t status = AImageReader_new(width, height, format, 2, &reader); ++#endif ++ if (status != AMEDIA_OK || !reader) ++ throw ResourceError("Failed to create AImageReader", nullptr, __FILE__, __LINE__); ++ ++ ANativeWindow *nativeWindow = nullptr; ++ status = AImageReader_getWindow(reader, &nativeWindow); ++ if (status != AMEDIA_OK || !nativeWindow) ++ { ++ AImageReader_delete(reader); ++ throw ResourceError("Failed to get window from AImageReader", nullptr, __FILE__, __LINE__); ++ } ++ ++ *outReader = reader; ++ return nativeWindow; ++} ++ ++class ImageReaderNativeWindow : public eglu::NativeWindow ++{ ++public: ++ ImageReaderNativeWindow(AImageReader *reader, ANativeWindow *window, int width, int height) ++ : eglu::NativeWindow(WINDOW_CAPABILITIES) ++ , m_reader(reader) ++ , m_window(window) ++ , m_size(width, height) ++ { ++ } ++ ++ virtual ~ImageReaderNativeWindow(void) ++ { ++ if (m_reader) ++ AImageReader_delete(m_reader); ++ } ++ ++ virtual eglw::EGLNativeWindowType getLegacyNative(void) ++ { ++ return m_window; ++ } ++ virtual void *getPlatformExtension(void) ++ { ++ return m_window; ++ } ++ virtual void *getPlatformNative(void) ++ { ++ return m_window; ++ } ++ tcu::IVec2 getScreenSize(void) const ++ { ++ return m_size; ++ } ++ void setSurfaceSize(tcu::IVec2 size) ++ { ++ int32_t format = 0; // 0 means keep the existing format ++ ANativeWindow_setBuffersGeometry(m_window, size.x(), size.y(), format); ++ m_size = size; ++ } ++ virtual void processEvents(void) ++ { ++ } ++ ++private: ++ AImageReader *m_reader; ++ ANativeWindow *m_window; ++ tcu::IVec2 m_size; ++}; ++#endif ++ + // NativeWindow + + NativeWindow::NativeWindow(Window *window, int width, int height, int32_t format) +@@ -197,10 +278,29 @@ eglu::NativeWindow *NativeWindowFactory::createWindow(const eglu::WindowParams & + { + Window *window = m_windowRegistry.tryAcquireWindow(); + +- if (!window) +- throw NotSupportedError("Native window is not available", nullptr, __FILE__, __LINE__); +- +- return new NativeWindow(window, params.width, params.height, format); ++ if (window) ++ { ++ return new NativeWindow(window, params.width, params.height, format); ++ } ++ else ++ { ++#if DE_ANDROID_API >= 24 ++ int width = params.width != eglu::WindowParams::SIZE_DONT_CARE ? params.width : 256; ++ int height = params.height != eglu::WindowParams::SIZE_DONT_CARE ? params.height : 256; ++ width = width > 0 ? width : 256; ++ height = height > 0 ? height : 256; ++ ++ AImageReader *reader = nullptr; ++ // Always use AIMAGE_FORMAT_RGBA_8888: the AImageReader is only used as a ++ // surface handle provider, and AIMAGE_FORMAT_* constants are not ++ // interchangeable with ANativeWindow_LegacyFormat values. ++ ANativeWindow *nativeWindow = acquireImageReaderWindow(width, height, AIMAGE_FORMAT_RGBA_8888, &reader); ++ ++ return new ImageReaderNativeWindow(reader, nativeWindow, width, height); ++#else ++ throw ResourceError("Native window is not available", nullptr, __FILE__, __LINE__); ++#endif ++ } + } + + // NativeDisplayFactory +@@ -279,6 +379,43 @@ private: + tcu::Android::Window &m_window; + }; + ++#if DE_ANDROID_API >= 24 ++class ImageReaderVulkanWindow : public vk::wsi::AndroidWindowInterface ++{ ++public: ++ ImageReaderVulkanWindow(AImageReader *reader, ANativeWindow *window) ++ : vk::wsi::AndroidWindowInterface(vk::pt::AndroidNativeWindowPtr(window)) ++ , m_reader(reader) ++ { ++ } ++ ++ void setVisible(bool visible) ++ { ++ DE_UNREF(visible); ++ } ++ ++ void resize(const UVec2 &newSize) ++ { ++ DE_UNREF(newSize); ++ } ++ ++ void setMinimized(bool minimized) ++ { ++ DE_UNREF(minimized); ++ TCU_THROW(NotSupportedError, "Minimized on Android is not implemented"); ++ } ++ ++ ~ImageReaderVulkanWindow(void) ++ { ++ if (m_reader) ++ AImageReader_delete(m_reader); ++ } ++ ++private: ++ AImageReader *m_reader; ++}; ++#endif ++ + class VulkanDisplay : public vk::wsi::Display + { + public: +@@ -306,7 +443,21 @@ public: + } + } + else ++ { ++#if DE_ANDROID_API >= 24 ++ uint32_t width = initialSize ? initialSize->x() : 256; ++ uint32_t height = initialSize ? initialSize->y() : 256; ++ width = width > 0 ? width : 256; ++ height = height > 0 ? height : 256; ++ ++ AImageReader *reader = nullptr; ++ ANativeWindow *nativeWindow = acquireImageReaderWindow(width, height, AIMAGE_FORMAT_RGBA_8888, &reader); ++ ++ return new ImageReaderVulkanWindow(reader, nativeWindow); ++#else + TCU_THROW(ResourceError, "Native window is not available"); ++#endif ++ } + } + + private: +diff --git a/targets/android/android.cmake b/targets/android/android.cmake +index 33843fcc09..6da33d3c17 100644 +--- a/targets/android/android.cmake ++++ b/targets/android/android.cmake +@@ -64,6 +64,11 @@ if (DE_ANDROID_API GREATER 8) + set(DEQP_PLATFORM_LIBRARIES ${DEQP_PLATFORM_LIBRARIES} ${ANDROID_LIBRARY}) + endif () + ++if (DE_ANDROID_API GREATER 23) ++ find_library(MEDIANDK_LIBRARY NAMES mediandk PATHS /usr/lib) ++ set(DEQP_PLATFORM_LIBRARIES ${DEQP_PLATFORM_LIBRARIES} ${MEDIANDK_LIBRARY}) ++endif () ++ + # Android uses customized execserver + include_directories(execserver) + set(DEQP_PLATFORM_LIBRARIES xscore ${DEQP_PLATFORM_LIBRARIES}) +-- +2.51.0 + diff --git a/.gitlab-ci/container/patches/build-deqp-gl_Build-Don-t-build-Vulkan-utilities-for-GL-builds.patch b/.gitlab-ci/container/patches/build-deqp-gl_Build-Don-t-build-Vulkan-utilities-for-GL-builds.patch index 2be1bc89312..c9ba0dc014e 100644 --- a/.gitlab-ci/container/patches/build-deqp-gl_Build-Don-t-build-Vulkan-utilities-for-GL-builds.patch +++ b/.gitlab-ci/container/patches/build-deqp-gl_Build-Don-t-build-Vulkan-utilities-for-GL-builds.patch @@ -1,4 +1,4 @@ -From ccdc2b9341c703507cba6017d2a494595335ffdc Mon Sep 17 00:00:00 2001 +From 8a9b46a0fd740679ff576ae70e5ac4bae12ac8b0 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 29 Jan 2025 12:50:33 +0000 Subject: [PATCH] Build: Don't build Vulkan utilities for GL builds @@ -6,15 +6,15 @@ Subject: [PATCH] Build: Don't build Vulkan utilities for GL builds Change-Id: Ie412f914bb6264ffbd502deea57d80cc11a9948e Signed-off-by: Daniel Stone --- - framework/platform/CMakeLists.txt | 9 -- - .../platform/android/tcuAndroidPlatform.cpp | 126 +----------------- + framework/platform/CMakeLists.txt | 9 - + .../platform/android/tcuAndroidPlatform.cpp | 177 +----------------- .../platform/android/tcuAndroidPlatform.hpp | 14 +- framework/platform/lnx/tcuLnxPlatform.cpp | 9 +- - .../surfaceless/tcuSurfacelessPlatform.cpp | 54 -------- - 5 files changed, 3 insertions(+), 209 deletions(-) + .../surfaceless/tcuSurfacelessPlatform.cpp | 54 ------ + 5 files changed, 3 insertions(+), 260 deletions(-) diff --git a/framework/platform/CMakeLists.txt b/framework/platform/CMakeLists.txt -index ec1deb5c2..49fe412a7 100644 +index 64d248b30f..833df5a08a 100644 --- a/framework/platform/CMakeLists.txt +++ b/framework/platform/CMakeLists.txt @@ -15,8 +15,6 @@ if (NOT DEFINED TCUTIL_PLATFORM_SRCS) @@ -35,7 +35,7 @@ index ec1deb5c2..49fe412a7 100644 ) include_directories(lnx) -@@ -164,8 +160,6 @@ if (NOT DEFINED TCUTIL_PLATFORM_SRCS) +@@ -168,8 +164,6 @@ if (NOT DEFINED TCUTIL_PLATFORM_SRCS) set(TCUTIL_PLATFORM_SRCS osx/tcuOSXPlatform.cpp osx/tcuOSXPlatform.hpp @@ -44,7 +44,7 @@ index ec1deb5c2..49fe412a7 100644 osx/tcuOSXMetalView.mm osx/tcuOSXMetalView.hpp ) -@@ -191,9 +185,6 @@ if (DEQP_USE_WAYLAND) +@@ -195,9 +189,6 @@ if (DEQP_USE_WAYLAND) add_dependencies(tcutil-platform deqp-xdg-shell) endif() @@ -55,7 +55,7 @@ index ec1deb5c2..49fe412a7 100644 # Always link to glutil as some platforms such as Win32 always support GL diff --git a/framework/platform/android/tcuAndroidPlatform.cpp b/framework/platform/android/tcuAndroidPlatform.cpp -index af56dabb8..6b0de6dba 100644 +index 9b117f50c6..b007c783cf 100644 --- a/framework/platform/android/tcuAndroidPlatform.cpp +++ b/framework/platform/android/tcuAndroidPlatform.cpp @@ -31,7 +31,6 @@ @@ -66,7 +66,7 @@ index af56dabb8..6b0de6dba 100644 // Assume no call translation is needed #include -@@ -217,102 +216,6 @@ eglu::NativeDisplay *NativeDisplayFactory::createDisplay(const EGLAttrib *attrib +@@ -309,153 +308,6 @@ eglu::NativeDisplay *NativeDisplayFactory::createDisplay(const EGLAttrib *attrib return new NativeDisplay(); } @@ -132,6 +132,43 @@ index af56dabb8..6b0de6dba 100644 - tcu::Android::Window &m_window; -}; - +-#if DE_ANDROID_API >= 24 +-class ImageReaderVulkanWindow : public vk::wsi::AndroidWindowInterface +-{ +-public: +- ImageReaderVulkanWindow(AImageReader *reader, ANativeWindow *window) +- : vk::wsi::AndroidWindowInterface(vk::pt::AndroidNativeWindowPtr(window)) +- , m_reader(reader) +- { +- } +- +- void setVisible(bool visible) +- { +- DE_UNREF(visible); +- } +- +- void resize(const UVec2 &newSize) +- { +- DE_UNREF(newSize); +- } +- +- void setMinimized(bool minimized) +- { +- DE_UNREF(minimized); +- TCU_THROW(NotSupportedError, "Minimized on Android is not implemented"); +- } +- +- ~ImageReaderVulkanWindow(void) +- { +- if (m_reader) +- AImageReader_delete(m_reader); +- } +- +-private: +- AImageReader *m_reader; +-}; +-#endif +- -class VulkanDisplay : public vk::wsi::Display -{ -public: @@ -159,7 +196,21 @@ index af56dabb8..6b0de6dba 100644 - } - } - else +- { +-#if DE_ANDROID_API >= 24 +- uint32_t width = initialSize ? initialSize->x() : 256; +- uint32_t height = initialSize ? initialSize->y() : 256; +- width = width > 0 ? width : 256; +- height = height > 0 ? height : 256; +- +- AImageReader *reader = nullptr; +- ANativeWindow *nativeWindow = acquireImageReaderWindow(width, height, AIMAGE_FORMAT_RGBA_8888, &reader); +- +- return new ImageReaderVulkanWindow(reader, nativeWindow); +-#else - TCU_THROW(ResourceError, "Native window is not available"); +-#endif +- } - } - -private: @@ -169,7 +220,7 @@ index af56dabb8..6b0de6dba 100644 static size_t getTotalSystemMemory(ANativeActivity *activity) { const size_t MiB = (size_t)(1 << 20); -@@ -341,8 +244,7 @@ static size_t getTotalSystemMemory(ANativeActivity *activity) +@@ -484,8 +336,7 @@ static size_t getTotalSystemMemory(ANativeActivity *activity) // Platform Platform::Platform(NativeActivity &activity) @@ -179,7 +230,7 @@ index af56dabb8..6b0de6dba 100644 { m_nativeDisplayFactoryRegistry.registerFactory(new NativeDisplayFactory(m_windowRegistry)); m_contextFactoryRegistry.registerFactory(new eglu::GLContextFactory(m_nativeDisplayFactoryRegistry)); -@@ -358,16 +260,6 @@ bool Platform::processEvents(void) +@@ -501,16 +352,6 @@ bool Platform::processEvents(void) return true; } @@ -196,7 +247,7 @@ index af56dabb8..6b0de6dba 100644 void Platform::getMemoryLimits(tcu::PlatformMemoryLimits &limits) const { // Worst-case estimates -@@ -401,22 +293,6 @@ void Platform::getMemoryLimits(tcu::PlatformMemoryLimits &limits) const +@@ -544,22 +385,6 @@ void Platform::getMemoryLimits(tcu::PlatformMemoryLimits &limits) const limits.devicePageTableHierarchyLevels = 3; } @@ -220,7 +271,7 @@ index af56dabb8..6b0de6dba 100644 } // namespace tcu diff --git a/framework/platform/android/tcuAndroidPlatform.hpp b/framework/platform/android/tcuAndroidPlatform.hpp -index 32cc9068c..3c3f02d98 100644 +index 32cc9068c4..3c3f02d98d 100644 --- a/framework/platform/android/tcuAndroidPlatform.hpp +++ b/framework/platform/android/tcuAndroidPlatform.hpp @@ -27,7 +27,6 @@ @@ -267,7 +318,7 @@ index 32cc9068c..3c3f02d98 100644 const size_t m_totalSystemMemory; }; diff --git a/framework/platform/lnx/tcuLnxPlatform.cpp b/framework/platform/lnx/tcuLnxPlatform.cpp -index 8c0a3ef06..6b1a4985a 100644 +index 8c0a3ef06e..6b1a4985a1 100644 --- a/framework/platform/lnx/tcuLnxPlatform.cpp +++ b/framework/platform/lnx/tcuLnxPlatform.cpp @@ -23,12 +23,10 @@ @@ -309,7 +360,7 @@ index 8c0a3ef06..6b1a4985a 100644 #if defined(DEQP_SUPPORT_GLX) m_glPlatform.registerFactory(x11::glx::createContextFactory(m_eventState)); diff --git a/framework/platform/surfaceless/tcuSurfacelessPlatform.cpp b/framework/platform/surfaceless/tcuSurfacelessPlatform.cpp -index 585bfbbae..9f386b3ca 100644 +index 2224f4164e..c9f044b130 100644 --- a/framework/platform/surfaceless/tcuSurfacelessPlatform.cpp +++ b/framework/platform/surfaceless/tcuSurfacelessPlatform.cpp @@ -43,7 +43,6 @@ @@ -388,5 +439,5 @@ index 585bfbbae..9f386b3ca 100644 class ContextFactory : public glu::ContextFactory -- -2.45.2 +2.51.0 diff --git a/.gitlab-ci/image-tags.yml b/.gitlab-ci/image-tags.yml index d99532c0898..ba98f9cb6c5 100644 --- a/.gitlab-ci/image-tags.yml +++ b/.gitlab-ci/image-tags.yml @@ -23,7 +23,7 @@ variables: DEBIAN_BUILD_TAG: "20260430-imgui.2" DEBIAN_TEST_BASE_TAG: "20260502-virgl" - DEBIAN_TEST_ANDROID_TAG: "20260430-imgui.2" + DEBIAN_TEST_ANDROID_TAG: "20260505-wsi" DEBIAN_TEST_GL_TAG: "20260506-vvl-84" DEBIAN_TEST_VIDEO_TAG: "20260430-imgui.2" DEBIAN_TEST_VK_TAG: "20260430-vkd3d-2"