From d6df5d1ee6495b60d5452ec035408fcd67c7d291 Mon Sep 17 00:00:00 2001 From: Pyry Haulos Date: Wed, 20 Apr 2016 14:39:18 -0700 Subject: [PATCH] Derive available system memory from Android CDD Android CDD defines minimum available system memory based on screen size and density. Includes various cleanups and robustness improvements to JNI code in tcuAndroidUtil.cpp. Bug: 28275297 Change-Id: I727cdf47db8e8f5d765017ed348a316234fe4f8e --- framework/platform/android/tcuAndroidPlatform.cpp | 38 +- framework/platform/android/tcuAndroidPlatform.hpp | 1 + framework/platform/android/tcuAndroidUtil.cpp | 558 ++++++++++++++++++---- framework/platform/android/tcuAndroidUtil.hpp | 33 ++ 4 files changed, 539 insertions(+), 91 deletions(-) diff --git a/framework/platform/android/tcuAndroidPlatform.cpp b/framework/platform/android/tcuAndroidPlatform.cpp index 40057e2..6d3fc48 100644 --- a/framework/platform/android/tcuAndroidPlatform.cpp +++ b/framework/platform/android/tcuAndroidPlatform.cpp @@ -261,10 +261,35 @@ private: WindowRegistry& m_windowRegistry; }; +static size_t getTotalSystemMemory (ANativeActivity* activity) +{ + const size_t MiB = (size_t)(1<<20); + + try + { + const size_t cddRequiredSize = getCDDRequiredSystemMemory(activity); + + print("Device has at least %.2f MiB total system memory per Android CDD\n", double(cddRequiredSize) / double(MiB)); + + return cddRequiredSize; + } + catch (const std::exception& e) + { + // Use relatively high fallback size to encourage CDD-compliant behavior + const size_t fallbackSize = (sizeof(void*) == sizeof(deUint64)) ? 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_activity (activity) + , m_totalSystemMemory (getTotalSystemMemory(activity.getNativeActivity())) { m_nativeDisplayFactoryRegistry.registerFactory(new NativeDisplayFactory(m_windowRegistry)); m_contextFactoryRegistry.registerFactory(new eglu::GLContextFactory(m_nativeDisplayFactoryRegistry)); @@ -292,8 +317,17 @@ void Platform::describePlatform (std::ostream& dst) const void Platform::getMemoryLimits (vk::PlatformMemoryLimits& limits) const { - limits.totalSystemMemory = 128*1024*1024; + // Worst-case estimates + const size_t MiB = (size_t)(1<<20); + const size_t baseMemUsage = 400*MiB; + const double safeUsageRatio = 0.25; + + limits.totalSystemMemory = de::max((size_t)(double(deInt64(m_totalSystemMemory)-deInt64(baseMemUsage)) * safeUsageRatio), 16*MiB); + + // Assume UMA architecture limits.totalDeviceLocalMemory = 0; + + // Reasonable worst-case estimates limits.deviceMemoryAllocationGranularity = 64*1024; limits.devicePageSize = 4096; limits.devicePageTableEntrySize = 8; diff --git a/framework/platform/android/tcuAndroidPlatform.hpp b/framework/platform/android/tcuAndroidPlatform.hpp index b5df4b2..1798777 100644 --- a/framework/platform/android/tcuAndroidPlatform.hpp +++ b/framework/platform/android/tcuAndroidPlatform.hpp @@ -59,6 +59,7 @@ public: private: NativeActivity& m_activity; WindowRegistry m_windowRegistry; + const size_t m_totalSystemMemory; }; } // Android diff --git a/framework/platform/android/tcuAndroidUtil.cpp b/framework/platform/android/tcuAndroidUtil.cpp index 3c09ce9..c62ee05 100644 --- a/framework/platform/android/tcuAndroidUtil.cpp +++ b/framework/platform/android/tcuAndroidUtil.cpp @@ -23,6 +23,9 @@ #include "tcuAndroidUtil.hpp" +#include "deSTLUtil.hpp" +#include "deMath.h" + #include namespace tcu @@ -33,7 +36,81 @@ namespace Android using std::string; using std::vector; -void checkJNIException (JNIEnv* env) +namespace +{ + +class ScopedJNIEnv +{ +public: + + ScopedJNIEnv (JavaVM* vm); + ~ScopedJNIEnv (void); + + JavaVM* getVM (void) const { return m_vm; } + JNIEnv* getEnv (void) const { return m_env; } + +private: + JavaVM* const m_vm; + JNIEnv* m_env; + bool m_detach; +}; + +ScopedJNIEnv::ScopedJNIEnv (JavaVM* vm) + : m_vm (vm) + , m_env (DE_NULL) + , m_detach (false) +{ + const int getEnvRes = m_vm->GetEnv((void**)&m_env, JNI_VERSION_1_6); + + if (getEnvRes == JNI_EDETACHED) + { + if (m_vm->AttachCurrentThread(&m_env, DE_NULL) != JNI_OK) + throw std::runtime_error("JNI AttachCurrentThread() failed"); + + m_detach = true; + } + else if (getEnvRes != JNI_OK) + throw std::runtime_error("JNI GetEnv() failed"); + + DE_ASSERT(m_env); +} + +ScopedJNIEnv::~ScopedJNIEnv (void) +{ + if (m_detach) + m_vm->DetachCurrentThread(); +} + +class LocalRef +{ +public: + LocalRef (JNIEnv* env, jobject ref); + ~LocalRef (void); + + jobject operator* (void) const { return m_ref; } + operator bool (void) const { return !!m_ref; } + +private: + LocalRef (const LocalRef&); + LocalRef& operator= (const LocalRef&); + + JNIEnv* const m_env; + const jobject m_ref; +}; + +LocalRef::LocalRef (JNIEnv* env, jobject ref) + : m_env(env) + , m_ref(ref) +{ +} + +LocalRef::~LocalRef (void) +{ + if (m_ref) + m_env->DeleteLocalRef(m_ref); +} + +void checkException (JNIEnv* env) { if (env->ExceptionCheck()) { @@ -43,7 +120,37 @@ void checkJNIException (JNIEnv* env) } } -string getJNIStringValue (JNIEnv* env, jstring jniStr) +jclass findClass (JNIEnv* env, const char* className) +{ + const jclass cls = env->FindClass(className); + + checkException(env); + TCU_CHECK_INTERNAL(cls); + + return cls; +} + +jclass getObjectClass (JNIEnv* env, jobject object) +{ + const jclass cls = env->GetObjectClass(object); + + checkException(env); + TCU_CHECK_INTERNAL(cls); + + return cls; +} + +jmethodID getMethodID (JNIEnv* env, jclass cls, const char* methodName, const char* signature) +{ + const jmethodID id = env->GetMethodID(cls, methodName, signature); + + checkException(env); + TCU_CHECK_INTERNAL(id); + + return id; +} + +string getStringValue (JNIEnv* env, jstring jniStr) { const char* ptr = env->GetStringUTFChars(jniStr, DE_NULL); const string str = string(ptr); @@ -53,63 +160,37 @@ string getJNIStringValue (JNIEnv* env, jstring jniStr) return str; } -static string getIntentStringExtra (JNIEnv* env, jobject activity, const char* name) +string getIntentStringExtra (JNIEnv* env, jobject activity, const char* name) { // \todo [2013-05-12 pyry] Clean up references on error. - jclass activityCls = env->GetObjectClass(activity); - jobject intent = env->CallObjectMethod(activity, env->GetMethodID(activityCls, "getIntent", "()Landroid/content/Intent;")); - TCU_CHECK(intent); + const jclass activityCls = getObjectClass(env, activity); + const LocalRef intent (env, env->CallObjectMethod(activity, getMethodID(env, activityCls, "getIntent", "()Landroid/content/Intent;"))); + TCU_CHECK_INTERNAL(intent); - jstring extraName = env->NewStringUTF(name); - jclass intentCls = env->GetObjectClass(intent); - TCU_CHECK(extraName && intentCls); + const LocalRef extraName (env, env->NewStringUTF(name)); + const jclass intentCls = getObjectClass(env, *intent); + TCU_CHECK_INTERNAL(extraName && intentCls); jvalue getExtraArgs[1]; - getExtraArgs[0].l = extraName; - jstring extraStr = (jstring)env->CallObjectMethodA(intent, env->GetMethodID(intentCls, "getStringExtra", "(Ljava/lang/String;)Ljava/lang/String;"), getExtraArgs); + getExtraArgs[0].l = *extraName; - env->DeleteLocalRef(extraName); + const LocalRef extraStr (env, env->CallObjectMethodA(*intent, getMethodID(env, intentCls, "getStringExtra", "(Ljava/lang/String;)Ljava/lang/String;"), getExtraArgs)); if (extraStr) - return getJNIStringValue(env, extraStr); + return getStringValue(env, (jstring)*extraStr); else return string(); } -string getIntentStringExtra (ANativeActivity* activity, const char* name) +void setRequestedOrientation (JNIEnv* env, jobject activity, ScreenOrientation orientation) { - return getIntentStringExtra(activity->env, activity->clazz, name); -} - -static void setRequestedOrientation (JNIEnv* env, jobject activity, ScreenOrientation orientation) -{ - jclass activityCls = env->GetObjectClass(activity); - jmethodID setOrientationId = env->GetMethodID(activityCls, "setRequestedOrientation", "(I)V"); + const jclass activityCls = getObjectClass(env, activity); + const jmethodID setOrientationId = getMethodID(env, activityCls, "setRequestedOrientation", "(I)V"); env->CallVoidMethod(activity, setOrientationId, (int)orientation); } -void setRequestedOrientation (ANativeActivity* activity, ScreenOrientation orientation) -{ - setRequestedOrientation(activity->env, activity->clazz, orientation); -} - -ScreenOrientation mapScreenRotation (ScreenRotation rotation) -{ - switch (rotation) - { - case SCREENROTATION_UNSPECIFIED: return SCREEN_ORIENTATION_UNSPECIFIED; - case SCREENROTATION_0: return SCREEN_ORIENTATION_PORTRAIT; - case SCREENROTATION_90: return SCREEN_ORIENTATION_LANDSCAPE; - case SCREENROTATION_180: return SCREEN_ORIENTATION_REVERSE_PORTRAIT; - case SCREENROTATION_270: return SCREEN_ORIENTATION_REVERSE_LANDSCAPE; - default: - print("Warning: Unsupported rotation"); - return SCREEN_ORIENTATION_PORTRAIT; - } -} - template const char* getJNITypeStr (void); @@ -120,6 +201,12 @@ const char* getJNITypeStr (void) } template<> +const char* getJNITypeStr (void) +{ + return "F"; +} + +template<> const char* getJNITypeStr (void) { return "Ljava/lang/String;"; @@ -147,7 +234,7 @@ string getStaticFieldValue (JNIEnv* env, jclass cls, jfieldID fieldId) const jstring jniStr = (jstring)env->GetStaticObjectField(cls, fieldId); if (jniStr) - return getJNIStringValue(env, jniStr); + return getStringValue(env, jniStr); else return string(); } @@ -158,7 +245,7 @@ vector getStaticFieldValue > (JNIEnv* env, jclass cls, jf const jobjectArray array = (jobjectArray)env->GetStaticObjectField(cls, fieldId); vector result; - checkJNIException(env); + checkException(env); if (array) { @@ -168,10 +255,10 @@ vector getStaticFieldValue > (JNIEnv* env, jclass cls, jf { const jstring jniStr = (jstring)env->GetObjectArrayElement(array, ndx); - checkJNIException(env); + checkException(env); if (jniStr) - result.push_back(getJNIStringValue(env, jniStr)); + result.push_back(getStringValue(env, jniStr)); } } @@ -181,62 +268,50 @@ vector getStaticFieldValue > (JNIEnv* env, jclass cls, jf template FieldType getStaticField (JNIEnv* env, const char* className, const char* fieldName) { - const jclass cls = env->FindClass(className); - const jfieldID fieldId = cls ? env->GetStaticFieldID(cls, fieldName, getJNITypeStr()) : (jfieldID)0; + const jclass cls = findClass(env, className); + const jfieldID fieldId = env->GetStaticFieldID(cls, fieldName, getJNITypeStr()); - checkJNIException(env); + checkException(env); - if (cls && fieldId) + if (fieldId) return getStaticFieldValue(env, cls, fieldId); else - return FieldType(); + throw std::runtime_error(string(fieldName) + " not found in " + className); } -class ScopedJNIEnv -{ -public: - - ScopedJNIEnv (JavaVM* vm); - ~ScopedJNIEnv (void); - - JavaVM* getVM (void) const { return m_vm; } - JNIEnv* getEnv (void) const { return m_env; } - -private: - JavaVM* const m_vm; - JNIEnv* m_env; - bool m_detach; -}; +template +FieldType getFieldValue (JNIEnv* env, jobject obj, jfieldID fieldId); -ScopedJNIEnv::ScopedJNIEnv (JavaVM* vm) - : m_vm (vm) - , m_env (DE_NULL) - , m_detach (false) +template<> +int getFieldValue (JNIEnv* env, jobject obj, jfieldID fieldId) { - const int getEnvRes = m_vm->GetEnv((void**)&m_env, JNI_VERSION_1_6); - - if (getEnvRes == JNI_EDETACHED) - { - if (m_vm->AttachCurrentThread(&m_env, DE_NULL) != JNI_OK) - throw std::runtime_error("JNI AttachCurrentThread() failed"); - - m_detach = true; - } - else if (getEnvRes != JNI_OK) - throw std::runtime_error("JNI GetEnv() failed"); + DE_ASSERT(obj && fieldId); + return env->GetIntField(obj, fieldId); +} - DE_ASSERT(m_env); +template<> +float getFieldValue (JNIEnv* env, jobject obj, jfieldID fieldId) +{ + DE_ASSERT(obj && fieldId); + return env->GetFloatField(obj, fieldId); } -ScopedJNIEnv::~ScopedJNIEnv (void) +template +FieldType getField (JNIEnv* env, jobject obj, const char* fieldName) { - if (m_detach) - m_vm->DetachCurrentThread(); + const jclass cls = getObjectClass(env, obj); + const jfieldID fieldId = env->GetFieldID(cls, fieldName, getJNITypeStr()); + + checkException(env); + + if (fieldId) + return getFieldValue(env, obj, fieldId); + else + throw std::runtime_error(string(fieldName) + " not found in object"); } -void describePlatform (ANativeActivity* activity, std::ostream& dst) +void describePlatform (JNIEnv* env, std::ostream& dst) { - const ScopedJNIEnv env (activity->vm); const char* const buildClass = "android/os/Build"; const char* const versionClass = "android/os/Build$VERSION"; @@ -263,13 +338,13 @@ void describePlatform (ANativeActivity* activity, std::ostream& dst) for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_stringFields); ndx++) dst << s_stringFields[ndx].className << "." << s_stringFields[ndx].fieldName - << ": " << getStaticField(env.getEnv(), s_stringFields[ndx].classPath, s_stringFields[ndx].fieldName) + << ": " << getStaticField(env, s_stringFields[ndx].classPath, s_stringFields[ndx].fieldName) << "\n"; - dst << "Build.VERSION.SDK_INT: " << getStaticField(env.getEnv(), versionClass, "SDK_INT") << "\n"; + dst << "Build.VERSION.SDK_INT: " << getStaticField(env, versionClass, "SDK_INT") << "\n"; { - const vector supportedAbis = getStaticField >(env.getEnv(), buildClass, "SUPPORTED_ABIS"); + const vector supportedAbis = getStaticField >(env, buildClass, "SUPPORTED_ABIS"); dst << "Build.SUPPORTED_ABIS: "; @@ -280,5 +355,310 @@ void describePlatform (ANativeActivity* activity, std::ostream& dst) } } +vector getSupportedABIs (JNIEnv* env) +{ + return getStaticField >(env, "android/os/Build", "SUPPORTED_ABIS"); +} + +bool supportsAny64BitABI (JNIEnv* env) +{ + const vector supportedAbis = getSupportedABIs(env); + const char* known64BitAbis[] = { "arm64-v8a", "x86_64", "mips64" }; + + for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(known64BitAbis); ++ndx) + { + if (de::contains(supportedAbis.begin(), supportedAbis.end(), string(known64BitAbis[ndx]))) + return true; + } + + return false; +} + +bool supportsAny64BitABI (ANativeActivity* activity) +{ + const ScopedJNIEnv env(activity->vm); + + return supportsAny64BitABI(env.getEnv()); +} + +jobject getPackageManager (JNIEnv* env, jobject activity) +{ + const jclass activityCls = getObjectClass(env, activity); + const jmethodID getPMID = getMethodID(env, activityCls, "getPackageManager", "()Landroid/content/pm/PackageManager;"); + const jobject packageManager = env->CallObjectMethod(activity, getPMID); + + return packageManager; +} + +bool hasSystemFeature (JNIEnv* env, jobject activity, const char* name) +{ + const LocalRef packageManager (env, getPackageManager(env, activity)); + const jclass pmCls = getObjectClass(env, *packageManager); + const jmethodID hasFeatureID = getMethodID(env, pmCls, "hasSystemFeature", "(Ljava/lang/String;)Z"); + const LocalRef nameStr (env, env->NewStringUTF(name)); + jvalue callArgs[1]; + + callArgs[0].l = *nameStr; + + return env->CallBooleanMethodA(*packageManager, hasFeatureID, callArgs) == JNI_TRUE; +} + +jobject getWindowManager (JNIEnv* env, jobject activity) +{ + const jclass activityCls = getObjectClass(env, activity); + const jmethodID getWMID = getMethodID(env, activityCls, "getWindowManager", "()Landroid/view/WindowManager;"); + const jobject windowManager = env->CallObjectMethod(activity, getWMID); + + return windowManager; +} + +jobject getDefaultDisplay (JNIEnv* env, jobject windowManager) +{ + const jclass wmClass = getObjectClass(env, windowManager); + const jmethodID getDisplayID = getMethodID(env, wmClass, "getDefaultDisplay", "()Landroid/view/Display;"); + const jobject display = env->CallObjectMethod(windowManager, getDisplayID); + + return display; +} + +jobject createDisplayMetrics (JNIEnv* env) +{ + const jclass displayMetricsCls = findClass(env, "android/util/DisplayMetrics"); + const jmethodID ctorId = getMethodID(env, displayMetricsCls, "", "()V"); + + return env->NewObject(displayMetricsCls, ctorId); +} + +DisplayMetrics getDisplayMetrics (JNIEnv* env, jobject activity) +{ + const LocalRef windowManager (env, getWindowManager(env, activity)); + const LocalRef defaultDisplay (env, getDefaultDisplay(env, *windowManager)); + const LocalRef nativeMetrics (env, createDisplayMetrics(env)); + const jclass displayCls = getObjectClass(env, *defaultDisplay); + const jmethodID getMetricsID = getMethodID(env, displayCls, "getMetrics", "(Landroid/util/DisplayMetrics;)V"); + DisplayMetrics metrics; + + { + jvalue callArgs[1]; + callArgs[0].l = *nativeMetrics; + + env->CallVoidMethodA(*defaultDisplay, getMetricsID, callArgs); + } + + metrics.density = getField (env, *nativeMetrics, "density"); + metrics.densityDpi = getField (env, *nativeMetrics, "densityDpi"); + metrics.scaledDensity = getField (env, *nativeMetrics, "scaledDensity"); + metrics.widthPixels = getField (env, *nativeMetrics, "widthPixels"); + metrics.heightPixels = getField (env, *nativeMetrics, "heightPixels"); + metrics.xdpi = getField (env, *nativeMetrics, "xdpi"); + metrics.ydpi = getField (env, *nativeMetrics, "ydpi"); + + return metrics; +} + +enum ScreenClass +{ + SCREEN_CLASS_WEAR = 0, + SCREEN_CLASS_SMALL, + SCREEN_CLASS_NORMAL, + SCREEN_CLASS_LARGE, + SCREEN_CLASS_EXTRA_LARGE, + + SCREEN_CLASS_LAST +}; + +enum DensityClass +{ + DENSITY_CLASS_LDPI = 120, + DENSITY_CLASS_MDPI = 160, + DENSITY_CLASS_TVDPI = 213, + DENSITY_CLASS_HDPI = 240, + DENSITY_CLASS_280DPI = 280, + DENSITY_CLASS_XHDPI = 320, + DENSITY_CLASS_360DPI = 360, + DENSITY_CLASS_400DPI = 400, + DENSITY_CLASS_420DPI = 420, + DENSITY_CLASS_XXHDPI = 480, + DENSITY_CLASS_560DPI = 560, + DENSITY_CLASS_XXXHDPI = 640, + + DENSITY_CLASS_INVALID = -1, +}; + +ScreenClass getScreenClass (const DisplayMetrics& displayMetrics) +{ + static const struct + { + int minWidthDp; + int minHeightDp; + ScreenClass screenClass; + } s_screenClasses[] = + { + // Must be ordered from largest to smallest + { 960, 720, SCREEN_CLASS_EXTRA_LARGE }, + { 640, 480, SCREEN_CLASS_LARGE }, + { 480, 320, SCREEN_CLASS_NORMAL }, + { 426, 320, SCREEN_CLASS_SMALL }, + }; + + const float dpScale = float(displayMetrics.densityDpi) / 160.f; + + // \note Assume landscape orientation for comparison + const int widthP = de::max(displayMetrics.widthPixels, displayMetrics.heightPixels); + const int heightP = de::min(displayMetrics.widthPixels, displayMetrics.heightPixels); + + const int widthDp = deFloorFloatToInt32(float(widthP) / dpScale); + const int heightDp = deFloorFloatToInt32(float(heightP) / dpScale); + + for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_screenClasses); ++ndx) + { + if ((s_screenClasses[ndx].minWidthDp <= widthDp) && + (s_screenClasses[ndx].minHeightDp <= heightDp)) + return s_screenClasses[ndx].screenClass; + } + + return SCREEN_CLASS_WEAR; +} + +bool isValidDensityClass (int dpi) +{ + switch (dpi) + { + case DENSITY_CLASS_LDPI: + case DENSITY_CLASS_MDPI: + case DENSITY_CLASS_TVDPI: + case DENSITY_CLASS_HDPI: + case DENSITY_CLASS_280DPI: + case DENSITY_CLASS_XHDPI: + case DENSITY_CLASS_360DPI: + case DENSITY_CLASS_400DPI: + case DENSITY_CLASS_420DPI: + case DENSITY_CLASS_XXHDPI: + case DENSITY_CLASS_560DPI: + case DENSITY_CLASS_XXXHDPI: + return true; + + default: + return false; + } +} + +DensityClass getDensityClass (const DisplayMetrics& displayMetrics) +{ + if (isValidDensityClass(displayMetrics.densityDpi)) + return (DensityClass)displayMetrics.densityDpi; + else + return DENSITY_CLASS_INVALID; +} + +} // anonymous + +ScreenOrientation mapScreenRotation (ScreenRotation rotation) +{ + switch (rotation) + { + case SCREENROTATION_UNSPECIFIED: return SCREEN_ORIENTATION_UNSPECIFIED; + case SCREENROTATION_0: return SCREEN_ORIENTATION_PORTRAIT; + case SCREENROTATION_90: return SCREEN_ORIENTATION_LANDSCAPE; + case SCREENROTATION_180: return SCREEN_ORIENTATION_REVERSE_PORTRAIT; + case SCREENROTATION_270: return SCREEN_ORIENTATION_REVERSE_LANDSCAPE; + default: + print("Warning: Unsupported rotation"); + return SCREEN_ORIENTATION_PORTRAIT; + } +} + +string getIntentStringExtra (ANativeActivity* activity, const char* name) +{ + const ScopedJNIEnv env(activity->vm); + + return getIntentStringExtra(env.getEnv(), activity->clazz, name); +} + +void setRequestedOrientation (ANativeActivity* activity, ScreenOrientation orientation) +{ + const ScopedJNIEnv env(activity->vm); + + setRequestedOrientation(env.getEnv(), activity->clazz, orientation); +} + +void describePlatform (ANativeActivity* activity, std::ostream& dst) +{ + const ScopedJNIEnv env(activity->vm); + + describePlatform(env.getEnv(), dst); +} + +bool hasSystemFeature (ANativeActivity* activity, const char* name) +{ + const ScopedJNIEnv env(activity->vm); + + return hasSystemFeature(env.getEnv(), activity->clazz, name); +} + +DisplayMetrics getDisplayMetrics (ANativeActivity* activity) +{ + const ScopedJNIEnv env(activity->vm); + + return getDisplayMetrics(env.getEnv(), activity->clazz); +} + +size_t getCDDRequiredSystemMemory (ANativeActivity* activity) +{ + const DisplayMetrics displayMetrics = getDisplayMetrics(activity); + const ScreenClass screenClass = getScreenClass(displayMetrics); + const bool isWearDevice = hasSystemFeature(activity, "android.hardware.type.watch"); + const bool is64BitDevice = supportsAny64BitABI(activity); + const size_t MiB = (size_t)(1<<20); + + if (!is64BitDevice) + TCU_CHECK_INTERNAL(sizeof(void*) != sizeof(deUint64)); + + if (isWearDevice) + { + TCU_CHECK_INTERNAL(!is64BitDevice); + return 416*MiB; + } + else + { + const DensityClass densityClass = getDensityClass(displayMetrics); + + TCU_CHECK_INTERNAL(de::inRange(screenClass, SCREEN_CLASS_SMALL, SCREEN_CLASS_EXTRA_LARGE)); + TCU_CHECK_INTERNAL(densityClass != DENSITY_CLASS_INVALID); + + static const struct + { + DensityClass smallNormalScreenDensity; + DensityClass largeScreenDensity; + DensityClass extraLargeScreenDensity; + size_t requiredMem32bit; + size_t requiredMem64bit; + } s_classes[] = + { + // Must be ordered from largest to smallest + { DENSITY_CLASS_560DPI, DENSITY_CLASS_400DPI, DENSITY_CLASS_XHDPI, 1344*MiB, 1824*MiB }, + { DENSITY_CLASS_400DPI, DENSITY_CLASS_XHDPI, DENSITY_CLASS_TVDPI, 896*MiB, 1280*MiB }, + { DENSITY_CLASS_XHDPI, DENSITY_CLASS_HDPI, DENSITY_CLASS_MDPI, 512*MiB, 832*MiB }, + + // \note Last is default, and density values are maximum allowed + { DENSITY_CLASS_280DPI, DENSITY_CLASS_MDPI, DENSITY_CLASS_LDPI, 424*MiB, 704*MiB }, + }; + + for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_classes); ++ndx) + { + const DensityClass minClass = screenClass == SCREEN_CLASS_EXTRA_LARGE ? s_classes[ndx].extraLargeScreenDensity + : screenClass == SCREEN_CLASS_LARGE ? s_classes[ndx].largeScreenDensity + : /* small/normal */ s_classes[ndx].smallNormalScreenDensity; + const size_t reqMem = is64BitDevice ? s_classes[ndx].requiredMem64bit : s_classes[ndx].requiredMem32bit; + const bool isLast = ndx == DE_LENGTH_OF_ARRAY(s_classes)-1; + + if ((isLast && minClass >= densityClass) || (!isLast && minClass <= densityClass)) + return reqMem; + } + + TCU_THROW(InternalError, "Invalid combination of density and screen size"); + } +} + } // Android } // tcu diff --git a/framework/platform/android/tcuAndroidUtil.hpp b/framework/platform/android/tcuAndroidUtil.hpp index 254e890..78601a6 100644 --- a/framework/platform/android/tcuAndroidUtil.hpp +++ b/framework/platform/android/tcuAndroidUtil.hpp @@ -52,6 +52,39 @@ ScreenOrientation mapScreenRotation (ScreenRotation rotation); void describePlatform (ANativeActivity* activity, std::ostream& dst); +bool hasSystemFeature (ANativeActivity* activity, const char* name); + +//! android.util.DisplayMetrics +struct DisplayMetrics +{ + float density; + float scaledDensity; + int densityDpi; + + int widthPixels; + int heightPixels; + + float xdpi; + float ydpi; + + DisplayMetrics (void) + : density (0.0f) + , scaledDensity (0.0f) + , densityDpi (0) + , widthPixels (0) + , heightPixels (0) + , xdpi (0.0f) + , ydpi (0.0f) + { + } +}; + +DisplayMetrics getDisplayMetrics (ANativeActivity* activity); + +//! Get minimum required system memory that must be available to kernel and +//! userspace according to Android CDD. +size_t getCDDRequiredSystemMemory (ANativeActivity* activity); + } // Android } // tcu -- 2.7.4