/*------------------------------------------------------------------------- * drawElements Quality Program Tester Core * ---------------------------------------- * * Copyright 2014 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. * *//*! * \file * \brief Android EGL and Vulkan platforms. *//*--------------------------------------------------------------------*/ #include "tcuAndroidPlatform.hpp" #include "tcuAndroidUtil.hpp" #include "gluRenderContext.hpp" #include "egluNativeDisplay.hpp" #include "egluNativeWindow.hpp" #include "egluGLContextFactory.hpp" #include "egluUtil.hpp" #include "eglwLibrary.hpp" #include "eglwEnums.hpp" #include "tcuFunctionLibrary.hpp" #include "vkWsiPlatform.hpp" // Assume no call translation is needed #include 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 *)); DE_STATIC_ASSERT(sizeof(eglw::EGLNativeWindowType) == sizeof(ANativeWindow *)); namespace tcu { namespace Android { using namespace eglw; static const eglu::NativeDisplay::Capability DISPLAY_CAPABILITIES = eglu::NativeDisplay::CAPABILITY_GET_DISPLAY_LEGACY; static const eglu::NativeWindow::Capability WINDOW_CAPABILITIES = (eglu::NativeWindow::Capability)( eglu::NativeWindow::CAPABILITY_CREATE_SURFACE_LEGACY | eglu::NativeWindow::CAPABILITY_CREATE_SURFACE_PLATFORM | eglu::NativeWindow::CAPABILITY_CREATE_SURFACE_PLATFORM_EXTENSION | eglu::NativeWindow::CAPABILITY_SET_SURFACE_SIZE | eglu::NativeWindow::CAPABILITY_GET_SCREEN_SIZE); class NativeDisplay : public eglu::NativeDisplay { public: NativeDisplay(void) : eglu::NativeDisplay(DISPLAY_CAPABILITIES), m_library("libEGL.so") { } virtual ~NativeDisplay(void) { } virtual EGLNativeDisplayType getLegacyNative(void) { return EGL_DEFAULT_DISPLAY; } virtual const eglw::Library &getLibrary(void) const { return m_library; } private: eglw::DefaultLibrary m_library; }; class NativeDisplayFactory : public eglu::NativeDisplayFactory { public: NativeDisplayFactory(WindowRegistry &windowRegistry); ~NativeDisplayFactory(void) { } virtual eglu::NativeDisplay *createDisplay(const EGLAttrib *attribList) const; }; class NativeWindow : public eglu::NativeWindow { public: NativeWindow(Window *window, int width, int height, int32_t format); virtual ~NativeWindow(void); virtual EGLNativeWindowType getLegacyNative(void) { return m_window->getNativeWindow(); } virtual EGLNativeWindowType getPlatformExtension(void) { return m_window->getNativeWindow(); } virtual EGLNativeWindowType getPlatformNative(void) { return m_window->getNativeWindow(); } IVec2 getScreenSize(void) const { return m_window->getSize(); } void setSurfaceSize(IVec2 size); virtual void processEvents(void); private: Window *m_window; int32_t m_format; }; class NativeWindowFactory : public eglu::NativeWindowFactory { public: NativeWindowFactory(WindowRegistry &windowRegistry); ~NativeWindowFactory(void); virtual eglu::NativeWindow *createWindow(eglu::NativeDisplay *nativeDisplay, const eglu::WindowParams ¶ms) const; virtual eglu::NativeWindow *createWindow(eglu::NativeDisplay *nativeDisplay, EGLDisplay display, EGLConfig config, const EGLAttrib *attribList, const eglu::WindowParams ¶ms) const; private: virtual eglu::NativeWindow *createWindow(const eglu::WindowParams ¶ms, int32_t format) const; WindowRegistry &m_windowRegistry; }; // NativeWindow NativeWindow::NativeWindow(Window *window, int width, int height, int32_t format) : eglu::NativeWindow(WINDOW_CAPABILITIES) , m_window(window) , m_format(format) { // Set up buffers. setSurfaceSize(IVec2(width, height)); } NativeWindow::~NativeWindow(void) { m_window->release(); } void NativeWindow::processEvents(void) { if (m_window->isPendingDestroy()) throw eglu::WindowDestroyedError("Window has been destroyed"); } void NativeWindow::setSurfaceSize(tcu::IVec2 size) { m_window->setBuffersGeometry(size.x() != eglu::WindowParams::SIZE_DONT_CARE ? size.x() : 0, size.y() != eglu::WindowParams::SIZE_DONT_CARE ? size.y() : 0, m_format); } // NativeWindowFactory NativeWindowFactory::NativeWindowFactory(WindowRegistry &windowRegistry) : eglu::NativeWindowFactory("default", "Default display", WINDOW_CAPABILITIES) , m_windowRegistry(windowRegistry) { } NativeWindowFactory::~NativeWindowFactory(void) { } eglu::NativeWindow *NativeWindowFactory::createWindow(eglu::NativeDisplay *nativeDisplay, const eglu::WindowParams ¶ms) const { DE_UNREF(nativeDisplay); return createWindow(params, WINDOW_FORMAT_RGBA_8888); } eglu::NativeWindow *NativeWindowFactory::createWindow(eglu::NativeDisplay *nativeDisplay, EGLDisplay display, EGLConfig config, const EGLAttrib *attribList, const eglu::WindowParams ¶ms) const { const int32_t format = (int32_t)eglu::getConfigAttribInt(nativeDisplay->getLibrary(), display, config, EGL_NATIVE_VISUAL_ID); DE_UNREF(nativeDisplay && attribList); return createWindow(params, format); } eglu::NativeWindow *NativeWindowFactory::createWindow(const eglu::WindowParams ¶ms, int32_t format) const { Window *window = m_windowRegistry.tryAcquireWindow(); if (!window) throw ResourceError("Native window is not available", nullptr, __FILE__, __LINE__); return new NativeWindow(window, params.width, params.height, format); } // NativeDisplayFactory NativeDisplayFactory::NativeDisplayFactory(WindowRegistry &windowRegistry) : eglu::NativeDisplayFactory("default", "Default display", DISPLAY_CAPABILITIES) { m_nativeWindowRegistry.registerFactory(new NativeWindowFactory(windowRegistry)); } eglu::NativeDisplay *NativeDisplayFactory::createDisplay(const EGLAttrib *attribList) const { DE_UNREF(attribList); return new NativeDisplay(); } // Vulkan class VulkanLibrary : public vk::Library { public: VulkanLibrary(const char *libraryPath) : m_library(libraryPath != nullptr ? libraryPath : "libvulkan.so") , m_driver(m_library) { } const vk::PlatformInterface &getPlatformInterface(void) const { return m_driver; } const tcu::FunctionLibrary &getFunctionLibrary(void) const { return m_library; } private: const tcu::DynamicFunctionLibrary m_library; const vk::PlatformDriver m_driver; }; DE_STATIC_ASSERT(sizeof(vk::pt::AndroidNativeWindowPtr) == sizeof(ANativeWindow *)); class VulkanWindow : public vk::wsi::AndroidWindowInterface { public: VulkanWindow(tcu::Android::Window &window) : vk::wsi::AndroidWindowInterface(vk::pt::AndroidNativeWindowPtr(window.getNativeWindow())) , m_window(window) { } 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"); } ~VulkanWindow(void) { m_window.release(); } private: tcu::Android::Window &m_window; }; class VulkanDisplay : public vk::wsi::Display { public: VulkanDisplay(WindowRegistry &windowRegistry) : m_windowRegistry(windowRegistry) { } vk::wsi::Window *createWindow(const Maybe &initialSize) const { Window *const window = m_windowRegistry.tryAcquireWindow(); if (window) { try { if (initialSize) window->setBuffersGeometry((int)initialSize->x(), (int)initialSize->y(), WINDOW_FORMAT_RGBA_8888); return new VulkanWindow(*window); } catch (...) { window->release(); throw; } } else TCU_THROW(ResourceError, "Native window is not available"); } private: WindowRegistry &m_windowRegistry; }; static size_t getTotalSystemMemory(ANativeActivity *activity) { const size_t MiB = (size_t)(1 << 20); try { const size_t totalMemory = getTotalAndroidSystemMemory(activity); print("Device has %.2f MiB of system memory\n", static_cast(totalMemory) / static_cast(MiB)); return totalMemory; } catch (const std::exception &e) { // Use relatively high fallback size to encourage CDD-compliant behavior const size_t fallbackSize = (sizeof(void *) == sizeof(uint64_t)) ? 2048 * MiB : 1024 * MiB; print("WARNING: Failed to determine system memory size required by CDD: %s\n", e.what()); print("WARNING: Using fall-back size of %.2f MiB\n", double(fallbackSize) / double(MiB)); return fallbackSize; } } // Platform Platform::Platform(NativeActivity &activity) : m_activity(activity) , m_totalSystemMemory(getTotalSystemMemory(activity.getNativeActivity())) { m_nativeDisplayFactoryRegistry.registerFactory(new NativeDisplayFactory(m_windowRegistry)); m_contextFactoryRegistry.registerFactory(new eglu::GLContextFactory(m_nativeDisplayFactoryRegistry)); } Platform::~Platform(void) { } bool Platform::processEvents(void) { m_windowRegistry.garbageCollect(); return true; } vk::Library *Platform::createLibrary(const char *libraryPath) const { return new VulkanLibrary(libraryPath); } void Platform::describePlatform(std::ostream &dst) const { tcu::Android::describePlatform(m_activity.getNativeActivity(), dst); } void Platform::getMemoryLimits(tcu::PlatformMemoryLimits &limits) const { // Worst-case estimates const size_t MiB = (size_t)(1 << 20); const size_t baseMemUsage = 400 * MiB; #if (DE_PTR_SIZE == 4) // Some tests, such as: // // dEQP-VK.api.object_management.max_concurrent.* // dEQP-VK.memory.allocation.random.* // // when run in succession, can lead to system memory fragmentation. It depends on the allocator, and on some 32-bit // systems can lead to out of memory errors. As a workaround, we use a smaller amount of memory on 32-bit systems, // as this typically avoids out of memory errors caused by fragmentation. const double safeUsageRatio = 0.1; #else const double safeUsageRatio = 0.25; #endif limits.totalSystemMemory = de::max((size_t)(double(int64_t(m_totalSystemMemory) - int64_t(baseMemUsage)) * safeUsageRatio), 16 * MiB); // Assume UMA architecture limits.totalDeviceLocalMemory = 0; // Reasonable worst-case estimates limits.deviceMemoryAllocationGranularity = 64 * 1024; limits.devicePageSize = 4096; limits.devicePageTableEntrySize = 8; limits.devicePageTableHierarchyLevels = 3; } vk::wsi::Display *Platform::createWsiDisplay(vk::wsi::Type wsiType) const { if (wsiType == vk::wsi::TYPE_ANDROID) return new VulkanDisplay(const_cast(m_windowRegistry)); else TCU_THROW(NotSupportedError, "WSI type not supported on Android"); } bool Platform::hasDisplay(vk::wsi::Type wsiType) const { if (wsiType == vk::wsi::TYPE_ANDROID) return true; return false; } } // namespace Android } // namespace tcu