Commit a9d579e8 by Ian Elliott Committed by Commit Bot

Version-2 API of the A4A opt-in/out (a.k.a. feature-support utilities)

This version-2 API splits up and renames parts of the version-1 API. Some initial unit tests were added. The code is now built and tested on Linux, Windows, and Mac (in addition to Android, which is where it's being used). Memory leaks were fixed and some memory/list-management TODOs were also dealt with (associated with an Android bug). Some review items deferred. See: angleproject:2993 Bug: angleproject:2794 Bug: b/113346561 Change-Id: Iff307f9347fb05a733fe96b6774fb32d36e25590 Reviewed-on: https://chromium-review.googlesource.com/c/1291837Reviewed-by: 's avatarIan Elliott <ianelliott@google.com> Reviewed-by: 's avatarYuly Novikov <ynovikov@chromium.org> Commit-Queue: Ian Elliott <ianelliott@google.com>
parent 317a9ebd
...@@ -920,44 +920,53 @@ executable("angle_shader_translator") { ...@@ -920,44 +920,53 @@ executable("angle_shader_translator") {
] ]
} }
if (is_android) { config("angle_feature_support_config") {
config("angle_feature_support_config") { include_dirs = [
include_dirs = [ "include",
"include", "src",
"src", ]
] defines = [ "LIBFEATURE_SUPPORT_IMPLEMENTATION" ]
defines = [ "LIBFEATURE_SUPPORT_IMPLEMENTATION" ] if (is_debug) {
if (is_debug) { defines += [ "ANGLE_FEATURE_UTIL_LOG_VERBOSE" ]
defines += [ "ANGLE_FEATURE_UTIL_LOG_VERBOSE" ]
}
} }
}
shared_library("libfeature_support${angle_libs_suffix}") { shared_library("libfeature_support${angle_libs_suffix}") {
if (is_android) {
configs -= [ "//build/config/android:hide_all_but_jni_onload" ] configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
configs += [ ":build_id_config" ] configs += [ ":build_id_config" ]
configs -= angle_undefine_configs
configs += [
":internal_config",
":extra_warnings",
]
public_configs = [
":angle_feature_support_config",
"third_party/jsoncpp:jsoncpp_config",
]
libs = [ "log" ] libs = [ "log" ]
}
configs -= angle_undefine_configs
configs += [
":extra_warnings",
":internal_config",
]
sources = [ public_configs = [
"src/feature_support_util/angle_feature_support_util.h", ":angle_feature_support_config",
"src/feature_support_util/feature_support_util.cpp", "third_party/jsoncpp:jsoncpp_config",
"src/feature_support_util/feature_support_util.h", ]
]
deps = [ if (is_mac && !is_component_build) {
"third_party/jsoncpp:jsoncpp", ldflags = [
"-install_name",
"@rpath/${target_name}.dylib",
] ]
public_configs += [ ":shared_library_public_config" ]
} }
sources = [
"src/feature_support_util/feature_support_util.cpp",
"src/feature_support_util/feature_support_util.h",
]
deps = [
":angle_gpu_info_util",
"third_party/jsoncpp:jsoncpp",
]
}
if (is_android) {
# Package ANGLE libraries for pre-installed system image # Package ANGLE libraries for pre-installed system image
android_assets("angle_apk_assets") { android_assets("angle_apk_assets") {
disable_compression = true disable_compression = true
......
R"=====({"Rules": {
[ "Rules":[
{"Rule" : "Default Rule (i.e. use native driver)", {
"AppChoice" : true, "NonChoice" : false "Rule":"Default Rule (i.e. use native driver)",
}, "UseANGLE":false
{"Rule" : "Supported application(s) (e.g. Maps on Google devices)", },
"AppChoice" : true, "NonChoice" : true, {
"Applications" : "Rule":"Supported application(s) (e.g. Maps on Google devices)",
[ "UseANGLE":true,
{"AppName" : "com.google.android.apps.internal.cubey"}, "Applications":[
{"AppName" : "com.android.gl2jni"} {
], "AppName":"com.google.android.apps.internal.cubey"
"Devices" : },
[ {
{"Manufacturer" : "Google" "AppName":"com.android.gl2jni"
} }
],
"Devices":[
{
"Manufacturer":"Google"
}
]
}
] ]
}
]
} }
)====="
//
// Copyright 2018 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// angle_feature_support_util.h: External header file for feature-support utilities.
// feature_support_util.cpp: Helps Android EGL loader to determine whether to
// use ANGLE or a native GLES driver. Can be extended in the future for
// more-general feature selection.
#ifndef ANGLE_FEATURE_SUPPORT_UTIL_H_
#define ANGLE_FEATURE_SUPPORT_UTIL_H_
#if defined(ANDROID)
// FIXME - NEED TO #define ANGLE_EXPORT in a way that works for Android builds
# include "export.h"
#else
# include "export.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
// The following is the "version 1" external interface that the Android EGL loader used.
// Callers of the ANGLE feature-support-utility API (e.g. the Android EGL loader) will call this
// function in order to determine what version of the API it can use (if any).
//
// The caller supplies the highest version of the API that it knows about. If that version is
// supported by the feature-support-utility, true is returned and the version isn't changed. If
// the supplied version is higher than supported by the feature-support-utility, true is returned
// and the version is changed to the highest supported by the feature-support-utility. If the
// supplied version is lower than supported by the feature-support-utility, false is returned.
//
// Parameters:
//
// - versionToUse (IN/OUT) - The application supplies the highest version of the interface that it
// knows about. If successful, the output value is either unchanged or is the highest supported
// by the interface.
//
ANGLE_EXPORT bool ANGLEGetUtilityAPI(unsigned int *versionToUse);
// The Android EGL loader will call this function in order to determine whether
// to use ANGLE instead of a native OpenGL-ES (GLES) driver.
//
// Parameters:
// - rules_fd - File descriptor of the rules file to use
// - rules_offset - Offset into the fd before finding the contents of the rules file
// - rules_length - length of the rules file content
// - appName - Java name of the application (e.g. "com.google.android.apps.maps")
// - deviceMfr - Device manufacturer, from the "ro.product.manufacturer"com.google.android" property
// - deviceModel - Device model, from the "ro.product.model"com.google.android" property
//
ANGLE_EXPORT bool AndroidUseANGLEForApplication(int rules_fd,
long rules_offset,
long rules_length,
const char *appName,
const char *deviceMfr,
const char *deviceModel);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // ANGLE_FEATURE_SUPPORT_UTIL_H_
...@@ -4,40 +4,44 @@ ...@@ -4,40 +4,44 @@
// found in the LICENSE file. // found in the LICENSE file.
// //
// feature_support_util.cpp: Helps Android EGL loader to determine whether to use ANGLE or a native // feature_support_util.cpp: Helps client APIs make decisions based on rules
// GLES driver. Helps ANGLE know which work-arounds to use. // data files. For example, the Android EGL loader uses this library to
// determine whether to use ANGLE or a native GLES driver.
#include "feature_support_util.h" #include "feature_support_util.h"
#include <json/json.h> #include <json/json.h>
#include <string.h> #include <string.h>
#include "common/platform.h"
#if defined(ANGLE_PLATFORM_ANDROID)
#include <android/log.h>
#include <unistd.h> #include <unistd.h>
#endif
#include <fstream> #include <fstream>
#include <list> #include <list>
#include "../gpu_info_util/SystemInfo.h"
// namespace angle_for_android namespace angle
//{ {
#if defined(ANDROID)
# include <android/log.h>
// Define ANGLE_FEATURE_UTIL_LOG_VERBOSE if you want ALOGV to output #if defined(ANGLE_PLATFORM_ANDROID)
// Define ANGLE_FEATURE_UTIL_LOG_VERBOSE if you want VERBOSE to output
// ANGLE_FEATURE_UTIL_LOG_VERBOSE is automatically defined when is_debug = true // ANGLE_FEATURE_UTIL_LOG_VERBOSE is automatically defined when is_debug = true
# define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, "ANGLE", __VA_ARGS__) #define ERR(...) __android_log_print(ANDROID_LOG_ERROR, "ANGLE", __VA_ARGS__)
# define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, "ANGLE", __VA_ARGS__) #define WARN(...) __android_log_print(ANDROID_LOG_WARN, "ANGLE", __VA_ARGS__)
# define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, "ANGLE", __VA_ARGS__) #define INFO(...) __android_log_print(ANDROID_LOG_INFO, "ANGLE", __VA_ARGS__)
# define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "ANGLE", __VA_ARGS__) #define DEBUG(...) __android_log_print(ANDROID_LOG_DEBUG, "ANGLE", __VA_ARGS__)
# ifdef ANGLE_FEATURE_UTIL_LOG_VERBOSE #ifdef ANGLE_FEATURE_UTIL_LOG_VERBOSE
# define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "ANGLE", __VA_ARGS__) #define VERBOSE(...) __android_log_print(ANDROID_LOG_VERBOSE, "ANGLE", __VA_ARGS__)
# else #else
# define ALOGV(...) ((void)0) #define VERBOSE(...) ((void)0)
# endif #endif
#else // defined(ANDROID) #else // defined(ANDROID)
# define ALOGE(...) printf(__VA_ARGS__); #define ERR(...) printf(__VA_ARGS__);
# define ALOGW(...) printf(__VA_ARGS__); #define WARN(...) printf(__VA_ARGS__);
# define ALOGI(...) printf(__VA_ARGS__); #define INFO(...) printf(__VA_ARGS__);
# define ALOGD(...) printf(__VA_ARGS__); #define DEBUG(...) printf(__VA_ARGS__);
# define ALOGV(...) printf(__VA_ARGS__); #define VERBOSE(...) printf(__VA_ARGS__);
#endif // defined(ANDROID) #endif // defined(ANDROID)
// JSON values are generally composed of either: // JSON values are generally composed of either:
...@@ -47,21 +51,16 @@ ...@@ -47,21 +51,16 @@
// below, as follows: // below, as follows:
// The JSON identifier for the top-level set of rules. This is an object, the value of which is an // The JSON identifier for the top-level set of rules. This is an object, the value of which is an
// array of rules. The rules will be processed in order. For any given type of answer, if a rule // array of rules. The rules will be processed in order. If a rule matches, the rule's version of
// matches, the rule's version of the answer (true or false) becomes the new answer. After all // the answer (true or false) becomes the new answer. After all rules are processed, the
// rules are processed, the most-recent answer is the final answer. // most-recent answer is the final answer.
constexpr char kJsonRules[] = "Rules"; constexpr char kJsonRules[] = "Rules";
// The JSON identifier for a given rule. A rule is an object, the first string:value pair is this // The JSON identifier for a given rule. A rule is an object, the first string:value pair is this
// identifier (i.e. "Rule") as the string and a user-firendly description of the rule: // identifier (i.e. "Rule") as the string and the value is a user-firendly description of the rule:
constexpr char kJsonRule[] = "Rule"; constexpr char kJsonRule[] = "Rule";
// Within a rule, the JSON identifier for one type of answer--whether to allow an application to // Within a rule, the JSON identifier for the answer--whether or not to use ANGLE. The value is a
// specify whether to use ANGLE. The value is a boolean (i.e. true or false), with true allowing // boolean (i.e. true or false).
// the application to specify whether or not to use ANGLE. constexpr char kJsonUseANGLE[] = "UseANGLE";
constexpr char kJsonAppChoice[] = "AppChoice";
// Within a rule, the JSON identifier for one type of answer--whether or not to use ANGLE when an
// application doesn't specify (or isn't allowed to specify) whether or not to use ANGLE. The
// value is a boolean (i.e. true or false).
constexpr char kJsonNonChoice[] = "NonChoice";
// Within a rule, the JSON identifier for describing one or more applications. The value is an // Within a rule, the JSON identifier for describing one or more applications. The value is an
// array of objects, each object of which can specify attributes of an application. // array of objects, each object of which can specify attributes of an application.
...@@ -70,16 +69,13 @@ constexpr char kJsonApplications[] = "Applications"; ...@@ -70,16 +69,13 @@ constexpr char kJsonApplications[] = "Applications";
// name of the application (e.g. "com.google.maps"). The value is a string. If any other // name of the application (e.g. "com.google.maps"). The value is a string. If any other
// attributes will be specified, this must be the first attribute specified in the object. // attributes will be specified, this must be the first attribute specified in the object.
constexpr char kJsonAppName[] = "AppName"; constexpr char kJsonAppName[] = "AppName";
// Within an object that describes the attributes of an application, the JSON identifier for the
// intent of the application to run. The value is a string.
constexpr char kJsonIntent[] = "Intent";
// Within a rule, the JSON identifier for describing one or more devices. The value is an // Within a rule, the JSON identifier for describing one or more devices. The value is an
// array of objects, each object of which can specify attributes of a device. // array of objects, each object of which can specify attributes of a device.
constexpr char kJsonDevices[] = "Devices"; constexpr char kJsonDevices[] = "Devices";
// Within an object that describes the attributes of a device, the JSON identifier for the // Within an object that describes the attributes of a device, the JSON identifier for the
// manufacturer of the device. The value is a string. If any other attributes will be specified, // manufacturer of the device. The value is a string. If any other non-GPU attributes will be
// this must be the first attribute specified in the object. // specified, this must be the first attribute specified in the object.
constexpr char kJsonManufacturer[] = "Manufacturer"; constexpr char kJsonManufacturer[] = "Manufacturer";
// Within an object that describes the attributes of a device, the JSON identifier for the // Within an object that describes the attributes of a device, the JSON identifier for the
// model of the device. The value is a string. // model of the device. The value is a string.
...@@ -92,11 +88,11 @@ constexpr char kJsonGPUs[] = "GPUs"; ...@@ -92,11 +88,11 @@ constexpr char kJsonGPUs[] = "GPUs";
// Within an object that describes the attributes of a GPU and driver, the JSON identifier for the // Within an object that describes the attributes of a GPU and driver, the JSON identifier for the
// vendor of the device/driver. The value is a string. If any other attributes will be specified, // vendor of the device/driver. The value is a string. If any other attributes will be specified,
// this must be the first attribute specified in the object. // this must be the first attribute specified in the object.
constexpr char kJsonvendor[] = "vendor"; constexpr char kJsonVendor[] = "Vendor";
// Within an object that describes the attributes of a GPU and driver, the JSON identifier for the // Within an object that describes the attributes of a GPU and driver, the JSON identifier for the
// deviceId of the device. The value is an unsigned integer. If the driver version will be // deviceId of the device. The value is an unsigned integer. If the driver version will be
// specified, this must preceded the version attributes specified in the object. // specified, this must preceded the version attributes specified in the object.
constexpr char kJsondeviceId[] = "deviceId"; constexpr char kJsonDeviceId[] = "DeviceId";
// Within an object that describes the attributes of either an application or a GPU, the JSON // Within an object that describes the attributes of either an application or a GPU, the JSON
// identifier for the major version of that application or GPU driver. The value is a positive // identifier for the major version of that application or GPU driver. The value is a positive
...@@ -127,9 +123,9 @@ class StringPart ...@@ -127,9 +123,9 @@ class StringPart
{ {
public: public:
StringPart() : mPart(""), mWildcard(true) {} StringPart() : mPart(""), mWildcard(true) {}
StringPart(std::string part) : mPart(part), mWildcard(false) {} StringPart(const std::string part) : mPart(part), mWildcard(false) {}
~StringPart() {} ~StringPart() {}
bool match(StringPart &toCheck) bool match(const StringPart &toCheck) const
{ {
return (mWildcard || toCheck.mWildcard || (toCheck.mPart == mPart)); return (mWildcard || toCheck.mWildcard || (toCheck.mPart == mPart));
} }
...@@ -147,7 +143,7 @@ class IntegerPart ...@@ -147,7 +143,7 @@ class IntegerPart
IntegerPart() : mPart(0), mWildcard(true) {} IntegerPart() : mPart(0), mWildcard(true) {}
IntegerPart(uint32_t part) : mPart(part), mWildcard(false) {} IntegerPart(uint32_t part) : mPart(part), mWildcard(false) {}
~IntegerPart() {} ~IntegerPart() {}
bool match(IntegerPart &toCheck) bool match(const IntegerPart &toCheck) const
{ {
return (mWildcard || toCheck.mWildcard || (toCheck.mPart == mPart)); return (mWildcard || toCheck.mWildcard || (toCheck.mPart == mPart));
} }
...@@ -157,32 +153,32 @@ class IntegerPart ...@@ -157,32 +153,32 @@ class IntegerPart
bool mWildcard; bool mWildcard;
}; };
// This encapsulates a list of other classes, which of which will have a match() method. The // This encapsulates a list of other classes, each of which will have a match() and logItem()
// common constructor (given a type, but not any list items) assumes that this is a wildcard // method. The common constructor (given a type, but not any list items) assumes that this is
// (i.e. will match all other ListOf<t> objects). // a wildcard (i.e. will match all other ListOf<t> objects).
template <class T> template <class T>
class ListOf class ListOf
{ {
public: public:
ListOf(std::string listType) : mListType(listType), mWildcard(true) {} ListOf(const std::string listType) : mWildcard(true), mListType(listType) {}
~ListOf() {} ~ListOf() { mList.clear(); }
void addItem(T &toAdd) void addItem(const T &toAdd)
{ {
mList.push_back(toAdd); mList.push_back(toAdd);
mWildcard = false; mWildcard = false;
} }
bool match(T &toCheck) bool match(const T &toCheck) const
{ {
ALOGV("\t\t Within ListOf<%s> match: wildcards are %s and %s,\n", mListType.c_str(), VERBOSE("\t\t Within ListOf<%s> match: wildcards are %s and %s,\n", mListType.c_str(),
mWildcard ? "true" : "false", toCheck.mWildcard ? "true" : "false"); mWildcard ? "true" : "false", toCheck.mWildcard ? "true" : "false");
if (mWildcard || toCheck.mWildcard) if (mWildcard || toCheck.mWildcard)
{ {
return true; return true;
} }
for (auto &it : mList) for (const T &it : mList)
{ {
ALOGV("\t\t Within ListOf<%s> match: calling match on sub-item is %s,\n", VERBOSE("\t\t Within ListOf<%s> match: calling match on sub-item is %s,\n",
mListType.c_str(), it.match(toCheck) ? "true" : "false"); mListType.c_str(), it.match(toCheck) ? "true" : "false");
if (it.match(toCheck)) if (it.match(toCheck))
{ {
return true; return true;
...@@ -190,17 +186,17 @@ class ListOf ...@@ -190,17 +186,17 @@ class ListOf
} }
return false; return false;
} }
T &front() { return (mList.front()); } const T &front() const { return (mList.front()); }
void logListOf(std::string prefix, std::string name) void logListOf(const std::string prefix, const std::string name) const
{ {
if (mWildcard) if (mWildcard)
{ {
ALOGV("%sListOf%s is wildcarded to always match", prefix.c_str(), name.c_str()); VERBOSE("%sListOf%s is wildcarded to always match", prefix.c_str(), name.c_str());
} }
else else
{ {
ALOGV("%sListOf%s is has %d item(s):", prefix.c_str(), name.c_str(), VERBOSE("%sListOf%s has %d item(s):", prefix.c_str(), name.c_str(),
static_cast<int>(mList.size())); static_cast<int>(mList.size()));
for (auto &it : mList) for (auto &it : mList)
{ {
it.logItem(); it.logItem();
...@@ -208,12 +204,11 @@ class ListOf ...@@ -208,12 +204,11 @@ class ListOf
} }
} }
bool mWildcard;
private: private:
std::string mListType; std::string mListType;
std::list<T> mList; std::vector<T> mList;
public:
bool mWildcard = true;
}; };
// This encapsulates up-to four 32-bit unsigned integers, that represent a potentially-complex // This encapsulates up-to four 32-bit unsigned integers, that represent a potentially-complex
...@@ -240,25 +235,8 @@ class Version ...@@ -240,25 +235,8 @@ class Version
mWildcard(toCopy.mWildcard) mWildcard(toCopy.mWildcard)
{} {}
~Version() {} ~Version() {}
bool match(Version &toCheck)
{ static Version *CreateVersionFromJson(const Json::Value &jObject)
ALOGV("\t\t\t Within Version %d,%d,%d,%d match(%d,%d,%d,%d): wildcards are %s and %s,\n",
mMajor.mPart, mMinor.mPart, mSubminor.mPart, mPatch.mPart, toCheck.mMajor.mPart,
toCheck.mMinor.mPart, toCheck.mSubminor.mPart, toCheck.mPatch.mPart,
mWildcard ? "true" : "false", toCheck.mWildcard ? "true" : "false");
if (!(mWildcard || toCheck.mWildcard))
{
ALOGV("\t\t\t mMajor match is %s, mMinor is %s, mSubminor is %s, mPatch is %s\n",
mMajor.match(toCheck.mMajor) ? "true" : "false",
mMinor.match(toCheck.mMinor) ? "true" : "false",
mSubminor.match(toCheck.mSubminor) ? "true" : "false",
mPatch.match(toCheck.mPatch) ? "true" : "false");
}
return (mWildcard || toCheck.mWildcard ||
(mMajor.match(toCheck.mMajor) && mMinor.match(toCheck.mMinor) &&
mSubminor.match(toCheck.mSubminor) && mPatch.match(toCheck.mPatch)));
}
static Version *CreateVersionFromJson(Json::Value &jObject)
{ {
Version *version = nullptr; Version *version = nullptr;
// A major version must be provided before a minor, and so on: // A major version must be provided before a minor, and so on:
...@@ -291,11 +269,28 @@ class Version ...@@ -291,11 +269,28 @@ class Version
version = new Version(major); version = new Version(major);
} }
} }
// TODO (ianelliott@) (b/113346561) appropriately destruct lists and
// other items that get created from json parsing
return version; return version;
} }
std::string getString()
bool match(const Version &toCheck) const
{
VERBOSE("\t\t\t Within Version %d,%d,%d,%d match(%d,%d,%d,%d): wildcards are %s and %s,\n",
mMajor.mPart, mMinor.mPart, mSubminor.mPart, mPatch.mPart, toCheck.mMajor.mPart,
toCheck.mMinor.mPart, toCheck.mSubminor.mPart, toCheck.mPatch.mPart,
mWildcard ? "true" : "false", toCheck.mWildcard ? "true" : "false");
if (!(mWildcard || toCheck.mWildcard))
{
VERBOSE("\t\t\t mMajor match is %s, mMinor is %s, mSubminor is %s, mPatch is %s\n",
mMajor.match(toCheck.mMajor) ? "true" : "false",
mMinor.match(toCheck.mMinor) ? "true" : "false",
mSubminor.match(toCheck.mSubminor) ? "true" : "false",
mPatch.match(toCheck.mPatch) ? "true" : "false");
}
return (mWildcard || toCheck.mWildcard ||
(mMajor.match(toCheck.mMajor) && mMinor.match(toCheck.mMinor) &&
mSubminor.match(toCheck.mSubminor) && mPatch.match(toCheck.mPatch)));
}
std::string getString() const
{ {
if (mWildcard) if (mWildcard)
{ {
...@@ -342,100 +337,66 @@ class Version ...@@ -342,100 +337,66 @@ class Version
bool mWildcard; bool mWildcard;
}; };
// This encapsulates an application, and potentially the application's Version and/or the intent // This encapsulates an application, and potentially the application's Version. The default
// that it is launched with. The default constructor (not given any values) assumes that this is a // constructor (not given any values) assumes that this is a wildcard (i.e. will match all
// wildcard (i.e. will match all other Application objects). Each part of an Application is stored // other Application objects). Each part of an Application is stored in a class that may
// in a class that may also be wildcarded. // also be wildcarded.
class Application class Application
{ {
public: public:
Application(std::string name, Version &version, std::string intent) Application(const std::string name, const Version &version)
: mName(name), mVersion(version), mIntent(intent), mWildcard(false) : mName(name), mVersion(version), mWildcard(false)
{}
Application(std::string name, std::string intent)
: mName(name), mVersion(), mIntent(intent), mWildcard(false)
{} {}
Application(std::string name, Version &version) Application(const std::string name) : mName(name), mVersion(), mWildcard(false) {}
: mName(name), mVersion(version), mIntent(), mWildcard(false) Application() : mName(), mVersion(), mWildcard(true) {}
{}
Application(std::string name) : mName(name), mVersion(), mIntent(), mWildcard(false) {}
Application() : mName(), mVersion(), mIntent(), mWildcard(true) {}
~Application() {} ~Application() {}
bool match(Application &toCheck)
{ static Application *CreateApplicationFromJson(const Json::Value &jObject)
return (mWildcard || toCheck.mWildcard ||
(toCheck.mName.match(mName) && toCheck.mVersion.match(mVersion) &&
toCheck.mIntent.match(mIntent)));
}
static Application *CreateApplicationFromJson(Json::Value &jObject)
{ {
Application *application = nullptr; Application *application = nullptr;
// If an application is listed, the application's name is required: // If an application is listed, the application's name is required:
std::string appName = jObject[kJsonAppName].asString(); std::string appName = jObject[kJsonAppName].asString();
// The application's version and intent are optional: // The application's version is optional:
Version *version = Version::CreateVersionFromJson(jObject); Version *version = Version::CreateVersionFromJson(jObject);
if (version) if (version)
{ {
if (jObject.isMember(kJsonIntent) && jObject[kJsonIntent].isString()) application = new Application(appName, *version);
{ delete version;
application = new Application(appName, *version, jObject[kJsonIntent].asString());
}
else
{
application = new Application(appName, *version);
}
} }
else else
{ {
if (jObject.isMember(kJsonIntent) && jObject[kJsonIntent].isString()) application = new Application(appName);
{
application = new Application(appName, jObject[kJsonIntent].asString());
}
else
{
application = new Application(appName);
}
} }
// TODO (ianelliott@) (b/113346561) appropriately destruct lists and
// other items that get created from json parsing
return application; return application;
} }
void logItem()
bool match(const Application &toCheck) const
{
return (mWildcard || toCheck.mWildcard ||
(toCheck.mName.match(mName) && toCheck.mVersion.match(mVersion)));
}
void logItem() const
{ {
if (mWildcard) if (mWildcard)
{ {
ALOGV(" Wildcard (i.e. will match all applications)"); VERBOSE(" Wildcard (i.e. will match all applications)");
} }
else if (!mVersion.mWildcard) else if (!mVersion.mWildcard)
{ {
if (!mIntent.mWildcard) VERBOSE(" Application \"%s\" (version: %s)", mName.mPart.c_str(),
{ mVersion.getString().c_str());
ALOGV(" Application \"%s\" (version: %s; intent: \"%s\")", mName.mPart.c_str(),
mVersion.getString().c_str(), mIntent.mPart.c_str());
}
else
{
ALOGV(" Application \"%s\" (version: %s)", mName.mPart.c_str(),
mVersion.getString().c_str());
}
}
else if (!mIntent.mWildcard)
{
ALOGV(" Application \"%s\" (intent: \"%s\")", mName.mPart.c_str(),
mIntent.mPart.c_str());
} }
else else
{ {
ALOGV(" Application \"%s\"", mName.mPart.c_str()); VERBOSE(" Application \"%s\"", mName.mPart.c_str());
} }
} }
public: public:
StringPart mName; StringPart mName;
Version mVersion; Version mVersion;
StringPart mIntent;
bool mWildcard; bool mWildcard;
}; };
...@@ -445,53 +406,49 @@ class Application ...@@ -445,53 +406,49 @@ class Application
class GPU class GPU
{ {
public: public:
GPU(std::string vendor, uint32_t deviceId, Version &version) GPU(const std::string vendor, uint32_t deviceId, const Version &version)
: mVendor(vendor), mDeviceId(IntegerPart(deviceId)), mVersion(version), mWildcard(false) : mVendor(vendor), mDeviceId(IntegerPart(deviceId)), mVersion(version), mWildcard(false)
{} {}
GPU(uint32_t deviceId, Version &version) GPU(const std::string vendor, uint32_t deviceId)
: mVendor(), mDeviceId(IntegerPart(deviceId)), mVersion(version), mWildcard(false)
{}
GPU(std::string vendor, uint32_t deviceId)
: mVendor(vendor), mDeviceId(IntegerPart(deviceId)), mVersion(), mWildcard(false) : mVendor(vendor), mDeviceId(IntegerPart(deviceId)), mVersion(), mWildcard(false)
{} {}
GPU(std::string vendor) : mVendor(vendor), mDeviceId(), mVersion(), mWildcard(false) {} GPU(const std::string vendor) : mVendor(vendor), mDeviceId(), mVersion(), mWildcard(false) {}
GPU(uint32_t deviceId)
: mVendor(), mDeviceId(IntegerPart(deviceId)), mVersion(), mWildcard(false)
{}
GPU() : mVendor(), mDeviceId(), mVersion(), mWildcard(true) {} GPU() : mVendor(), mDeviceId(), mVersion(), mWildcard(true) {}
bool match(GPU &toCheck) bool match(const GPU &toCheck) const
{ {
ALOGV("\t\t Within GPU match: wildcards are %s and %s,\n", mWildcard ? "true" : "false", VERBOSE("\t\t Within GPU match: wildcards are %s and %s,\n", mWildcard ? "true" : "false",
toCheck.mWildcard ? "true" : "false"); toCheck.mWildcard ? "true" : "false");
ALOGV("\t\t mVendor = \"%s\" and toCheck.mVendor = \"%s\"\n", mVendor.mPart.c_str(), VERBOSE("\t\t mVendor = \"%s\" and toCheck.mVendor = \"%s\"\n", mVendor.mPart.c_str(),
toCheck.mVendor.mPart.c_str()); toCheck.mVendor.mPart.c_str());
ALOGV("\t\t mDeviceId = %d and toCheck.mDeviceId = %d\n", mDeviceId.mPart, VERBOSE("\t\t mDeviceId = %d and toCheck.mDeviceId = %d\n", mDeviceId.mPart,
toCheck.mDeviceId.mPart); toCheck.mDeviceId.mPart);
ALOGV("\t\t mVendor match is %s, mDeviceId is %s, mVersion is %s\n", VERBOSE("\t\t mVendor match is %s, mDeviceId is %s, mVersion is %s\n",
toCheck.mVendor.match(mVendor) ? "true" : "false", toCheck.mVendor.match(mVendor) ? "true" : "false",
toCheck.mDeviceId.match(mDeviceId) ? "true" : "false", toCheck.mDeviceId.match(mDeviceId) ? "true" : "false",
toCheck.mVersion.match(mVersion) ? "true" : "false"); toCheck.mVersion.match(mVersion) ? "true" : "false");
return (mWildcard || toCheck.mWildcard || return (mWildcard || toCheck.mWildcard ||
(toCheck.mVendor.match(mVendor) && toCheck.mDeviceId.match(mDeviceId) && (toCheck.mVendor.match(mVendor) && toCheck.mDeviceId.match(mDeviceId) &&
toCheck.mVersion.match(mVersion))); toCheck.mVersion.match(mVersion)));
} }
~GPU() {} ~GPU() {}
static GPU *CreateGpuFromJson(Json::Value &jObject)
static GPU *CreateGpuFromJson(const Json::Value &jObject)
{ {
GPU *gpu = nullptr; GPU *gpu = nullptr;
// If a GPU is listed, the vendor name is required: // If a GPU is listed, the vendor name is required:
if (jObject.isMember(kJsonvendor) && jObject[kJsonvendor].isString()) if (jObject.isMember(kJsonVendor) && jObject[kJsonVendor].isString())
{ {
std::string vendor = jObject[kJsonvendor].asString(); std::string vendor = jObject[kJsonVendor].asString();
// If a version is given, the deviceId is required: // If a version is given, the deviceId is required:
if (jObject.isMember(kJsondeviceId) && jObject[kJsondeviceId].isUInt()) if (jObject.isMember(kJsonDeviceId) && jObject[kJsonDeviceId].isUInt())
{ {
uint32_t deviceId = jObject[kJsondeviceId].asUInt(); uint32_t deviceId = jObject[kJsonDeviceId].asUInt();
Version *version = Version::CreateVersionFromJson(jObject); Version *version = Version::CreateVersionFromJson(jObject);
if (version) if (version)
{ {
gpu = new GPU(vendor, deviceId, *version); gpu = new GPU(vendor, deviceId, *version);
delete version;
} }
else else
{ {
...@@ -505,18 +462,17 @@ class GPU ...@@ -505,18 +462,17 @@ class GPU
} }
else else
{ {
ALOGW("Asked to parse a GPU, but no GPU found"); WARN("Asked to parse a GPU, but no vendor found");
} }
// TODO (ianelliott@) (b/113346561) appropriately destruct lists and
// other items that get created from json parsing
return gpu; return gpu;
} }
void logItem()
void logItem() const
{ {
if (mWildcard) if (mWildcard)
{ {
ALOGV(" Wildcard (i.e. will match all GPUs)"); VERBOSE(" Wildcard (i.e. will match all GPUs)");
} }
else else
{ {
...@@ -524,18 +480,18 @@ class GPU ...@@ -524,18 +480,18 @@ class GPU
{ {
if (!mVersion.mWildcard) if (!mVersion.mWildcard)
{ {
ALOGV("\t GPU vendor: %s, deviceId: 0x%x, version: %s", VERBOSE("\t GPU vendor: %s, deviceId: 0x%x, version: %s",
mVendor.mPart.c_str(), mDeviceId.mPart, mVersion.getString().c_str()); mVendor.mPart.c_str(), mDeviceId.mPart, mVersion.getString().c_str());
} }
else else
{ {
ALOGV("\t GPU vendor: %s, deviceId: 0x%x", mVendor.mPart.c_str(), VERBOSE("\t GPU vendor: %s, deviceId: 0x%x", mVendor.mPart.c_str(),
mDeviceId.mPart); mDeviceId.mPart);
} }
} }
else else
{ {
ALOGV("\t GPU vendor: %s", mVendor.mPart.c_str()); VERBOSE("\t GPU vendor: %s", mVendor.mPart.c_str());
} }
} }
} }
...@@ -554,34 +510,16 @@ class GPU ...@@ -554,34 +510,16 @@ class GPU
class Device class Device
{ {
public: public:
Device(std::string manufacturer, std::string model) Device(const std::string manufacturer, const std::string model)
: mManufacturer(manufacturer), mModel(model), mGpuList("GPU"), mWildcard(false) : mManufacturer(manufacturer), mModel(model), mGpuList("GPU"), mWildcard(false)
{} {}
Device(std::string manufacturer) Device(const std::string manufacturer)
: mManufacturer(manufacturer), mModel(), mGpuList("GPU"), mWildcard(false) : mManufacturer(manufacturer), mModel(), mGpuList("GPU"), mWildcard(false)
{} {}
Device() : mManufacturer(), mModel(), mGpuList("GPU"), mWildcard(true) {} Device() : mManufacturer(), mModel(), mGpuList("GPU"), mWildcard(true) {}
~Device() {} ~Device() {}
void addGPU(GPU &gpu) { mGpuList.addItem(gpu); }
bool match(Device &toCheck) static Device *CreateDeviceFromJson(const Json::Value &jObject)
{
ALOGV("\t Within Device match: wildcards are %s and %s,\n", mWildcard ? "true" : "false",
toCheck.mWildcard ? "true" : "false");
if (!(mWildcard || toCheck.mWildcard))
{
ALOGV("\t Manufacturer match is %s, model is %s\n",
toCheck.mManufacturer.match(mManufacturer) ? "true" : "false",
toCheck.mModel.match(mModel) ? "true" : "false");
}
ALOGV("\t Need to check ListOf<GPU>\n");
return ((mWildcard || toCheck.mWildcard ||
// The wildcards can override the Manufacturer/Model check, but not the GPU check
(toCheck.mManufacturer.match(mManufacturer) && toCheck.mModel.match(mModel))) &&
// Note: toCheck.mGpuList is for the device and must contain exactly one item,
// where mGpuList may contain zero or more items:
mGpuList.match(toCheck.mGpuList.front()));
}
static Device *CreateDeviceFromJson(Json::Value &jObject)
{ {
Device *device = nullptr; Device *device = nullptr;
if (jObject.isMember(kJsonManufacturer) && jObject[kJsonManufacturer].isString()) if (jObject.isMember(kJsonManufacturer) && jObject[kJsonManufacturer].isString())
...@@ -601,25 +539,44 @@ class Device ...@@ -601,25 +539,44 @@ class Device
else else
{ {
// This case is not treated as an error because a rule may wish to only call out one or // This case is not treated as an error because a rule may wish to only call out one or
// more GPUs, and not any specific Manufacturer devices: // more GPUs, but not any specific Manufacturer (e.g. for any manufacturer's device
// that uses a GPU from Vendor-A, with DeviceID-Foo, and with driver version 1.2.3.4):
device = new Device(); device = new Device();
} }
// TODO (ianelliott@) (b/113346561) appropriately destruct lists and
// other items that get created from json parsing
return device; return device;
} }
void logItem()
void addGPU(const GPU &gpu) { mGpuList.addItem(gpu); }
bool match(const Device &toCheck) const
{
VERBOSE("\t Within Device match: wildcards are %s and %s,\n", mWildcard ? "true" : "false",
toCheck.mWildcard ? "true" : "false");
if (!(mWildcard || toCheck.mWildcard))
{
VERBOSE("\t Manufacturer match is %s, model is %s\n",
toCheck.mManufacturer.match(mManufacturer) ? "true" : "false",
toCheck.mModel.match(mModel) ? "true" : "false");
}
VERBOSE("\t Need to check ListOf<GPU>\n");
return ((mWildcard || toCheck.mWildcard ||
// The wildcards can override the Manufacturer/Model check, but not the GPU check
(toCheck.mManufacturer.match(mManufacturer) && toCheck.mModel.match(mModel))) &&
// Note: toCheck.mGpuList is for the device and must contain exactly one item,
// where mGpuList may contain zero or more items:
mGpuList.match(toCheck.mGpuList.front()));
}
void logItem() const
{ {
if (mWildcard) if (mWildcard)
{ {
if (mGpuList.mWildcard) if (mGpuList.mWildcard)
{ {
ALOGV(" Wildcard (i.e. will match all devices)"); VERBOSE(" Wildcard (i.e. will match all devices)");
return; return;
} }
else else
{ {
ALOGV( VERBOSE(
" Device with any manufacturer and model" " Device with any manufacturer and model"
", and with the following GPUs:"); ", and with the following GPUs:");
} }
...@@ -628,14 +585,14 @@ class Device ...@@ -628,14 +585,14 @@ class Device
{ {
if (!mModel.mWildcard) if (!mModel.mWildcard)
{ {
ALOGV( VERBOSE(
" Device manufacturer: \"%s\" and model \"%s\"" " Device manufacturer: \"%s\" and model \"%s\""
", and with the following GPUs:", ", and with the following GPUs:",
mManufacturer.mPart.c_str(), mModel.mPart.c_str()); mManufacturer.mPart.c_str(), mModel.mPart.c_str());
} }
else else
{ {
ALOGV( VERBOSE(
" Device manufacturer: \"%s\"" " Device manufacturer: \"%s\""
", and with the following GPUs:", ", and with the following GPUs:",
mManufacturer.mPart.c_str()); mManufacturer.mPart.c_str());
...@@ -652,7 +609,7 @@ class Device ...@@ -652,7 +609,7 @@ class Device
}; };
// This encapsulates a particular scenario to check against the rules. A Scenario is similar to a // This encapsulates a particular scenario to check against the rules. A Scenario is similar to a
// Rule, except that a Rule has answers and potentially many wildcards, and a Scenario is the // Rule, except that a Rule has an answer and potentially many wildcards, and a Scenario is the
// fully-specified combination of an Application and a Device that is proposed to be run with // fully-specified combination of an Application and a Device that is proposed to be run with
// ANGLE. It is compared with the list of Rules. // ANGLE. It is compared with the list of Rules.
class Scenario class Scenario
...@@ -664,70 +621,53 @@ class Scenario ...@@ -664,70 +621,53 @@ class Scenario
~Scenario() {} ~Scenario() {}
void logScenario() void logScenario()
{ {
ALOGV(" Scenario to compare against the rules"); VERBOSE(" Scenario to compare against the rules");
ALOGV(" Application:"); VERBOSE(" Application:");
mApplication.logItem(); mApplication.logItem();
ALOGV(" Device:"); VERBOSE(" Device:");
mDevice.logItem(); mDevice.logItem();
} }
public: public:
Application mApplication; Application mApplication;
Device mDevice; Device mDevice;
private:
Scenario(Application &app, Device &dev) : mApplication(app), mDevice(dev) {}
Scenario() : mApplication(), mDevice() {}
}; };
// This encapsulates a Rule that provides answers based on whether a particular Scenario matches // This encapsulates a Rule that provides an answer based on whether a particular Scenario matches
// the Rule. A Rule always has answers, but can potentially wildcard every item in it (i.e. match // the Rule. A Rule always has an answer, but can potentially wildcard every item in it (i.e.
// every scenario). // match every scenario).
class Rule class Rule
{ {
public: public:
Rule(std::string description, bool appChoice, bool answer) Rule(const std::string description, bool useANGLE)
: mDescription(description), : mDescription(description),
mAppList("Application"), mAppList("Application"),
mDevList("Device"), mDevList("Device"),
mAppChoice(appChoice), mUseANGLE(useANGLE)
mAnswer(answer)
{} {}
~Rule() {} ~Rule() {}
void addApp(Application &app) { mAppList.addItem(app); } void addApp(const Application &app) { mAppList.addItem(app); }
void addDev(Device &dev) { mDevList.addItem(dev); } void addDevice(const Device &dev) { mDevList.addItem(dev); }
bool match(Scenario &toCheck) bool match(const Scenario &toCheck) const
{ {
ALOGV(" Within \"%s\" Rule: application match is %s and device match is %s\n", VERBOSE(" Within \"%s\" Rule: application match is %s and device match is %s\n",
mDescription.c_str(), mAppList.match(toCheck.mApplication) ? "true" : "false", mDescription.c_str(), mAppList.match(toCheck.mApplication) ? "true" : "false",
mDevList.match(toCheck.mDevice) ? "true" : "false"); mDevList.match(toCheck.mDevice) ? "true" : "false");
return (mAppList.match(toCheck.mApplication) && mDevList.match(toCheck.mDevice)); return (mAppList.match(toCheck.mApplication) && mDevList.match(toCheck.mDevice));
} }
bool getAppChoice() { return mAppChoice; } bool getUseANGLE() const { return mUseANGLE; }
bool getAnswer() { return mAnswer; } void logRule() const
void logRule()
{ {
ALOGV(" Rule: \"%s\" %s ANGLE, and %s the app a choice if matched", mDescription.c_str(), VERBOSE(" Rule: \"%s\" %s ANGLE", mDescription.c_str(),
mAnswer ? "enables" : "disables", mAppChoice ? "does give" : "does NOT give"); mUseANGLE ? "enables" : "disables");
mAppList.logListOf(" ", "Applications"); mAppList.logListOf(" ", "Applications");
mDevList.logListOf(" ", "Devices"); mDevList.logListOf(" ", "Devices");
} }
public:
std::string mDescription; std::string mDescription;
ListOf<Application> mAppList; ListOf<Application> mAppList;
ListOf<Device> mDevList; ListOf<Device> mDevList;
bool mAppChoice; bool mUseANGLE;
bool mAnswer;
private:
Rule()
: mDescription(),
mAppList("Application"),
mDevList("Device"),
mAppChoice(false),
mAnswer(false)
{}
}; };
// This encapsulates a list of Rules that Scenarios are matched against. A Scenario is compared // This encapsulates a list of Rules that Scenarios are matched against. A Scenario is compared
...@@ -737,178 +677,116 @@ class RuleList ...@@ -737,178 +677,116 @@ class RuleList
{ {
public: public:
RuleList() {} RuleList() {}
~RuleList() {} ~RuleList() { mRuleList.clear(); }
void addRule(Rule &rule) { mRuleList.push_back(rule); }
bool getAppChoice(Scenario &toCheck)
{
// Initialize the choice to the system-wide default (that should be set in the default
// rule, but just in case, set it here too):
bool appChoice = true;
ALOGV("Checking scenario against %d ANGLE-for-Android rules:",
static_cast<int>(mRuleList.size()));
for (auto &it : mRuleList) static RuleList *ReadRulesFromJsonString(const std::string jsonFileContents)
{
ALOGV(" Checking Rule: \"%s\" (to see whether there's a match)",
it.mDescription.c_str());
if (it.match(toCheck))
{
ALOGV(" -> Rule matches. Setting the app choice to %s",
it.getAppChoice() ? "true" : "false");
appChoice = it.getAppChoice();
}
else
{
ALOGV(" -> Rule doesn't match.");
}
}
return appChoice;
}
bool getAnswer(Scenario &toCheck)
{
// Initialize the answer to the system-wide default (that should be set in the default
// rule, but just in case, set it here too):
bool answer = false;
ALOGV("Checking scenario against %d ANGLE-for-Android rules:",
static_cast<int>(mRuleList.size()));
for (auto &it : mRuleList)
{
ALOGV(" Checking Rule: \"%s\" (to see whether there's a match)",
it.mDescription.c_str());
if (it.match(toCheck))
{
ALOGV(" -> Rule matches. Setting the answer to %s",
it.getAnswer() ? "true" : "false");
answer = it.getAnswer();
}
else
{
ALOGV(" -> Rule doesn't match.");
}
}
return answer;
}
static RuleList *ReadRulesFromJsonString(std::string jsonFileContents)
{ {
RuleList *rules = new RuleList; RuleList *rules = new RuleList;
// Open the file and start parsing it: // Open the file and start parsing it:
using namespace std;
Json::Reader jReader; Json::Reader jReader;
Json::Value jTopLevelObject; Json::Value jTopLevelObject;
jReader.parse(jsonFileContents, jTopLevelObject); jReader.parse(jsonFileContents, jTopLevelObject);
Json::Value jRules = jTopLevelObject[kJsonRules]; Json::Value jRules = jTopLevelObject[kJsonRules];
for (unsigned int i = 0; i < jRules.size(); i++) for (unsigned int ruleIndex = 0; ruleIndex < jRules.size(); ruleIndex++)
{ {
Json::Value jRule = jRules[i]; Json::Value jRule = jRules[ruleIndex];
std::string ruleDescription = jRule[kJsonRule].asString(); std::string ruleDescription = jRule[kJsonRule].asString();
bool ruleAppChoice = jRule[kJsonAppChoice].asBool(); bool useANGLE = jRule[kJsonUseANGLE].asBool();
bool ruleAnswer = jRule[kJsonNonChoice].asBool(); Rule *newRule = new Rule(ruleDescription, useANGLE);
// TODO (ianelliott@) (b/113346561) appropriately destruct lists and
// other items that get created from json parsing
Rule *newRule = new Rule(ruleDescription, ruleAppChoice, ruleAnswer);
Json::Value jApps = jRule[kJsonApplications]; Json::Value jApps = jRule[kJsonApplications];
for (unsigned int j = 0; j < jApps.size(); j++) for (unsigned int appIndex = 0; appIndex < jApps.size(); appIndex++)
{ {
Json::Value jApp = jApps[j]; Json::Value jApp = jApps[appIndex];
Application *newApp = Application::CreateApplicationFromJson(jApp); Application *newApp = Application::CreateApplicationFromJson(jApp);
// TODO (ianelliott@) (b/113346561) appropriately destruct lists and
// other items that get created from json parsing
newRule->addApp(*newApp); newRule->addApp(*newApp);
delete newApp;
} }
Json::Value jDevs = jRule[kJsonDevices]; Json::Value jDevs = jRule[kJsonDevices];
for (unsigned int j = 0; j < jDevs.size(); j++) for (unsigned int deviceIndex = 0; deviceIndex < jDevs.size(); deviceIndex++)
{ {
Json::Value jDev = jDevs[j]; Json::Value jDev = jDevs[deviceIndex];
Device *newDev = Device::CreateDeviceFromJson(jDev); Device *newDev = Device::CreateDeviceFromJson(jDev);
Json::Value jGPUs = jDev[kJsonGPUs]; Json::Value jGPUs = jDev[kJsonGPUs];
for (unsigned int k = 0; k < jGPUs.size(); k++) for (unsigned int gpuIndex = 0; gpuIndex < jGPUs.size(); gpuIndex++)
{ {
Json::Value jGPU = jGPUs[k]; Json::Value jGPU = jGPUs[gpuIndex];
GPU *newGPU = GPU::CreateGpuFromJson(jGPU); GPU *newGPU = GPU::CreateGpuFromJson(jGPU);
if (newGPU) if (newGPU)
{ {
newDev->addGPU(*newGPU); newDev->addGPU(*newGPU);
delete newGPU;
} }
} }
newRule->addDev(*newDev); newRule->addDevice(*newDev);
delete newDev;
} }
// TODO: Need to manage memory
rules->addRule(*newRule); rules->addRule(*newRule);
delete newRule;
} }
// Make sure there is at least one, default rule. If not, add it here: // Make sure there is at least one, default rule. If not, add it here:
int nRules = rules->mRuleList.size(); int nRules = rules->mRuleList.size();
if (nRules == 0) if (nRules == 0)
{ {
Rule defaultRule("Default Rule", true, false); Rule defaultRule("Default Rule", false);
rules->addRule(defaultRule); rules->addRule(defaultRule);
} }
return rules; return rules;
} }
void addRule(const Rule &rule) { mRuleList.push_back(rule); }
bool getUseANGLE(const Scenario &toCheck)
{
// Initialize useANGLE to the system-wide default (that should be set in the default
// rule, but just in case, set it here too):
bool useANGLE = false;
VERBOSE("Checking scenario against %d ANGLE-for-Android rules:",
static_cast<int>(mRuleList.size()));
for (const Rule &rule : mRuleList)
{
VERBOSE(" Checking Rule: \"%s\" (to see whether there's a match)",
rule.mDescription.c_str());
if (rule.match(toCheck))
{
VERBOSE(" -> Rule matches. Setting useANGLE to %s",
rule.getUseANGLE() ? "true" : "false");
useANGLE = rule.getUseANGLE();
}
else
{
VERBOSE(" -> Rule doesn't match.");
}
}
return useANGLE;
}
void logRules() void logRules()
{ {
ALOGV("Showing %d ANGLE-for-Android rules:", static_cast<int>(mRuleList.size())); VERBOSE("Showing %d ANGLE-for-Android rules:", static_cast<int>(mRuleList.size()));
for (auto &it : mRuleList) for (const Rule &rule : mRuleList)
{ {
it.logRule(); rule.logRule();
} }
} }
public: public:
std::list<Rule> mRuleList; std::vector<Rule> mRuleList;
}; };
//} // namespace angle_for_android } // namespace angle
extern "C" { extern "C" {
// using namespace angle_for_android; using namespace angle;
ANGLE_EXPORT bool ANGLEUseForApplication(const char *appName,
const char *deviceMfr,
const char *deviceModel,
ANGLEPreference developerOption,
ANGLEPreference appPreference)
{
Scenario scenario(appName, deviceMfr, deviceModel);
bool rtn = false;
scenario.logScenario();
// #include the contents of the file into a string and then parse it:
using namespace std;
// Embed the rules file contents into a string:
const char *s =
#include "a4a_rules.json"
;
std::string jsonFileContents = s;
RuleList *rules = RuleList::ReadRulesFromJsonString(jsonFileContents);
rules->logRules();
if (developerOption != ANGLE_NO_PREFERENCE)
{
rtn = (developerOption == ANGLE_PREFER_ANGLE);
}
else if ((appPreference != ANGLE_NO_PREFERENCE) && rules->getAppChoice(scenario))
{
rtn = (appPreference == ANGLE_PREFER_ANGLE);
}
else
{
rtn = rules->getAnswer(scenario);
}
ALOGV("Application \"%s\" should %s ANGLE", appName, rtn ? "use" : "NOT use");
delete rules;
return rtn;
}
#if defined(ANGLE_PLATFORM_ANDROID)
// This function is part of the NOW-DEPRECATED version-1 API:
ANGLE_EXPORT bool ANGLEGetUtilityAPI(unsigned int *versionToUse) ANGLE_EXPORT bool ANGLEGetUtilityAPI(unsigned int *versionToUse)
{ {
if (*versionToUse >= kFeatureVersion_LowestSupported) if (*versionToUse >= kFeatureVersion_LowestSupported)
...@@ -933,6 +811,7 @@ ANGLE_EXPORT bool ANGLEGetUtilityAPI(unsigned int *versionToUse) ...@@ -933,6 +811,7 @@ ANGLE_EXPORT bool ANGLEGetUtilityAPI(unsigned int *versionToUse)
} }
} }
// This function is part of the NOW-DEPRECATED version-1 API:
ANGLE_EXPORT bool AndroidUseANGLEForApplication(int rules_fd, ANGLE_EXPORT bool AndroidUseANGLEForApplication(int rules_fd,
long rules_offset, long rules_offset,
long rules_length, long rules_length,
...@@ -947,15 +826,11 @@ ANGLE_EXPORT bool AndroidUseANGLEForApplication(int rules_fd, ...@@ -947,15 +826,11 @@ ANGLE_EXPORT bool AndroidUseANGLEForApplication(int rules_fd,
// Read the contents of the file into a string and then parse it: // Read the contents of the file into a string and then parse it:
if (rules_fd < 0) if (rules_fd < 0)
{ {
ALOGW("Asked to read a non-open JSON file"); WARN("Asked to read a non-open JSON file");
return rtn; return rtn;
} }
off_t fileSize = rules_length; off_t fileSize = rules_length;
off_t startOfContent = rules_offset; off_t startOfContent = rules_offset;
// This is temporary magic--while there's extra stuff at the start of the file
// (so that it can be #include'd into the source code):
startOfContent += 8;
fileSize -= (8 + 7 + 2);
lseek(rules_fd, startOfContent, SEEK_SET); lseek(rules_fd, startOfContent, SEEK_SET);
char *buffer = new char[fileSize + 1]; char *buffer = new char[fileSize + 1];
ssize_t numBytesRead = read(rules_fd, buffer, fileSize); ssize_t numBytesRead = read(rules_fd, buffer, fileSize);
...@@ -965,11 +840,131 @@ ANGLE_EXPORT bool AndroidUseANGLEForApplication(int rules_fd, ...@@ -965,11 +840,131 @@ ANGLE_EXPORT bool AndroidUseANGLEForApplication(int rules_fd,
RuleList *rules = RuleList::ReadRulesFromJsonString(jsonFileContents); RuleList *rules = RuleList::ReadRulesFromJsonString(jsonFileContents);
rules->logRules(); rules->logRules();
rtn = rules->getAnswer(scenario); rtn = rules->getUseANGLE(scenario);
ALOGV("Application \"%s\" should %s ANGLE", appName, rtn ? "use" : "NOT use"); VERBOSE("Application \"%s\" should %s ANGLE", appName, rtn ? "use" : "NOT use");
delete rules; delete rules;
return rtn; return rtn;
} }
#endif // if defined(ANGLE_PLATFORM_ANDROID)
// This function is part of the version-2 API:
ANGLE_EXPORT bool ANGLEGetFeatureSupportUtilAPIVersion(unsigned int *versionToUse)
{
if (!versionToUse || (*versionToUse < kFeatureVersion_LowestSupported))
{
// The versionToUse is either nullptr or is less than the lowest version supported, which
// is an error.
return false;
}
if (*versionToUse > kFeatureVersion_HighestSupported)
{
// The versionToUse is greater than the highest version supported; change it to the
// highest version supported (caller will decide if it can use that version).
*versionToUse = kFeatureVersion_HighestSupported;
}
return true;
}
// This function is part of the version-2 API:
ANGLE_EXPORT bool ANGLEAndroidParseRulesString(const char *rulesString,
RulesHandle *rulesHandle,
int *rulesVersion)
{
if (!rulesString || !rulesHandle || !rulesVersion)
{
return false;
}
std::string rulesFileContents = rulesString;
RuleList *rules = RuleList::ReadRulesFromJsonString(rulesFileContents);
rules->logRules();
*rulesHandle = rules;
*rulesVersion = 0;
return true;
}
// This function is part of the version-2 API:
ANGLE_EXPORT bool ANGLEGetSystemInfo(SystemInfoHandle *systemInfoHandle)
{
if (!systemInfoHandle)
{
return false;
}
angle::SystemInfo *systemInfo = new angle::SystemInfo;
if (GetSystemInfo(systemInfo))
{
*systemInfoHandle = systemInfo;
return true;
}
return false;
}
// This function is part of the version-2 API:
ANGLE_EXPORT bool ANGLEAddDeviceInfoToSystemInfo(const char *deviceMfr,
const char *deviceModel,
SystemInfoHandle systemInfoHandle)
{
angle::SystemInfo *systemInfo = static_cast<angle::SystemInfo *>(systemInfoHandle);
if (!deviceMfr || !deviceModel || !systemInfo)
{
return false;
}
systemInfo->machineManufacturer = deviceMfr;
systemInfo->machineModelName = deviceModel;
return true;
}
// This function is part of the version-2 API:
ANGLE_EXPORT bool ANGLEShouldBeUsedForApplication(const RulesHandle rulesHandle,
int rulesVersion,
const SystemInfoHandle systemInfoHandle,
const char *appName)
{
RuleList *rules = static_cast<RuleList *>(rulesHandle);
angle::SystemInfo *systemInfo = static_cast<angle::SystemInfo *>(systemInfoHandle);
if (!rules || !systemInfo || !appName || (systemInfo->gpus.size() != 1))
{
return false;
}
Scenario scenario(appName, systemInfo->machineManufacturer.c_str(),
systemInfo->machineModelName.c_str());
Version gpuDriverVersion(systemInfo->gpus[0].detailedDriverVersion.major,
systemInfo->gpus[0].detailedDriverVersion.minor,
systemInfo->gpus[0].detailedDriverVersion.subMinor,
systemInfo->gpus[0].detailedDriverVersion.patch);
GPU gpuDriver(systemInfo->gpus[0].driverVendor, systemInfo->gpus[0].deviceId, gpuDriverVersion);
scenario.mDevice.addGPU(gpuDriver);
scenario.logScenario();
bool rtn = rules->getUseANGLE(scenario);
VERBOSE("Application \"%s\" should %s ANGLE", appName, rtn ? "use" : "NOT use");
return rtn;
}
// This function is part of the version-2 API:
ANGLE_EXPORT void ANGLEFreeRulesHandle(const RulesHandle rulesHandle)
{
RuleList *rules = static_cast<RuleList *>(rulesHandle);
if (rules)
{
delete rules;
}
}
// This function is part of the version-2 API:
ANGLE_EXPORT void ANGLEFreeSystemInfoHandle(const SystemInfoHandle systemInfoHandle)
{
angle::SystemInfo *systemInfo = static_cast<angle::SystemInfo *>(systemInfoHandle);
if (systemInfo)
{
delete systemInfo;
}
}
} // extern "C" } // extern "C"
...@@ -9,47 +9,164 @@ ...@@ -9,47 +9,164 @@
#ifndef FEATURE_SUPPORT_UTIL_H_ #ifndef FEATURE_SUPPORT_UTIL_H_
#define FEATURE_SUPPORT_UTIL_H_ #define FEATURE_SUPPORT_UTIL_H_
#include "angle_feature_support_util.h" #include "export.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
// The following are internal versions supported by the current feature-support-utility API. /**************************************************************************************************
*
constexpr unsigned int kFeatureVersion_LowestSupported = 0; * The following is the "version 1" private API for the Android EGL loader:
constexpr unsigned int kFeatureVersion_HighestSupported = 1; *
**************************************************************************************************/
// The following is the "version 0" external interface that the Android EGL loader used. It is // Callers of the ANGLE feature-support-utility API (e.g. the Android EGL loader) will call this
// deprecated and will soon be obsoleted. It was never declared in the shared header file. // function in order to determine what version of the API it can use (if any).
//
// TODO(ianelliott@google.com angleproject:2801): Revisit this enum. Make it // The caller supplies the highest version of the API that it knows about. If that version is
// strongly typed, and look at renaming it and its values. // supported by the feature-support-utility, true is returned and the version isn't changed. If
typedef enum ANGLEPreference // the supplied version is higher than supported by the feature-support-utility, true is
{ // returned and the version is changed to the highest supported by the feature-support-utility.
ANGLE_NO_PREFERENCE = 0, // If the supplied version is lower than supported by the feature-support-utility, false is
ANGLE_PREFER_NATIVE = 1, // returned.
ANGLE_PREFER_ANGLE = 2, //
} ANGLEPreference; // Parameters:
//
// - versionToUse (IN/OUT) - The application supplies the highest version of the interface that
// it knows about. If successful, the output value is either unchanged or is the highest
// supported by the interface.
//
ANGLE_EXPORT bool ANGLEGetUtilityAPI(unsigned int *versionToUse);
// The Android EGL loader will call this function in order to determine whether // The Android EGL loader will call this function in order to determine whether
// to use ANGLE instead of a native OpenGL-ES (GLES) driver. // to use ANGLE instead of a native OpenGL-ES (GLES) driver.
// //
// Parameters: // Parameters:
// - rules_fd - File descriptor of the rules file to use
// - rules_offset - Offset into the fd before finding the contents of the rules file
// - rules_length - length of the rules file content
// - appName - Java name of the application (e.g. "com.google.android.apps.maps") // - appName - Java name of the application (e.g. "com.google.android.apps.maps")
// - deviceMfr - Device manufacturer, from the "ro.product.manufacturer"com.google.android" property // - deviceMfr - Device manufacturer, from the "ro.product.manufacturer"com.google.android"
// property
// - deviceModel - Device model, from the "ro.product.model"com.google.android" property // - deviceModel - Device model, from the "ro.product.model"com.google.android" property
// - developerOption - Whether the "Developer Options" setting was set, and if so, how //
// - appPreference - Whether the application expressed a preference, and if so, how ANGLE_EXPORT bool AndroidUseANGLEForApplication(int rules_fd,
// long rules_offset,
// TODO(ianelliott@google.com angleproject:2801): Revisit this function long rules_length,
// name/interface. Look at generalizing it and making it more "feature" const char *appName,
// oriented. const char *deviceMfr,
ANGLE_EXPORT bool ANGLEUseForApplication(const char *appName, const char *deviceModel);
const char *deviceMfr,
const char *deviceModel, /**************************************************************************************************
ANGLEPreference developerOption, *
ANGLEPreference appPreference); * The following is the "version 2" private API for the Android EGL loader:
*
**************************************************************************************************/
// Typedefs for handles:
typedef void *RulesHandle;
typedef void *SystemInfoHandle;
// Callers of the ANGLE feature-support-utility API (e.g. the Android EGL loader) will call this
// function in order to determine what version of the API it can use (if any).
//
// The caller supplies the highest version of the API that it knows about. If that version is
// supported by the feature-support-utility, true is returned and the version isn't changed. If
// the supplied version is higher than supported by the feature-support-utility, true is
// returned and the version is changed to the highest supported by the feature-support-utility.
// If the supplied version is lower than supported by the feature-support-utility, false is
// returned.
//
// Parameters:
//
// - versionToUse (IN/OUT) - The application supplies the highest version of the interface that
// it knows about. If successful, the output value is either
// unchanged or is the highest supported by the interface.
//
ANGLE_EXPORT bool ANGLEGetFeatureSupportUtilAPIVersion(unsigned int *versionToUse);
// The Android EGL loader will call this function in order to parse a rules file
// and create a set of rules, for which a handle is returned.
//
// Parameters:
// - rulesString (IN) - Rules-file contents, as a non-zero length, null-terminated char*
// string
// - rulesHandle (OUT) - Handle to the rules data structure
// - rulesVersion (OUT) - Version of the rules data structure (potentially because of schema
// changes) that should be passed to ANGLEShouldBeUsedForApplication()
//
// Return value:
// - bool - true if no errors, otherwise false
//
ANGLE_EXPORT bool ANGLEAndroidParseRulesString(const char *rulesString,
RulesHandle *rulesHandle,
int *rulesVersion);
// The Android EGL loader will call this function in order to obtain a handle to
// the SystemInfo struct.
//
// Parameters:
// - systemInfoHandle (OUT) - handle to the SystemInfo structure
//
// Return value:
// - bool - true if no errors, otherwise false
//
ANGLE_EXPORT bool ANGLEGetSystemInfo(SystemInfoHandle *systemInfoHandle);
// The Android EGL loader will call this function in order to add the device's manufacturer and
// model to the SystemInfo struct associated with the handle returned by ANGLEGetSystemInfo.
//
// Parameters:
// - deviceMfr (IN) - Device manufacturer, from the
// "ro.product.manufacturer"com.google.android" property
// - deviceModel (IN) - Device model, from the "ro.product.model"com.google.android" property
// - systemInfoHandle (IN) - handle to the SystemInfo structure
//
// Return value:
// - bool - true if no errors, otherwise false
//
ANGLE_EXPORT bool ANGLEAddDeviceInfoToSystemInfo(const char *deviceMfr,
const char *deviceModel,
SystemInfoHandle systemInfoHandle);
// The Android EGL loader will call this function in order to determine whether
// to use ANGLE instead of a native OpenGL-ES (GLES) driver.
//
// Parameters:
// - rulesHandle (IN) - Handle to the rules data structure
// - rulesVersion (IN) - Version of the rules data structure (potentially because of schema
// changes) that was passed from AndroidParseRulesFile()
// - systemInfoHandle (IN) - Handle to the SystemInfo structure
// - appName (IN) - Java name of the application (e.g. "com.google.android.apps.maps")
//
// Return value:
// - bool - true if Android should use ANGLE for appName, otherwise false (i.e. use the native
// GLES driver)
//
ANGLE_EXPORT bool ANGLEShouldBeUsedForApplication(const RulesHandle rulesHandle,
int rulesVersion,
const SystemInfoHandle systemInfoHandle,
const char *appName);
// The Android EGL loader will call this function in order to free a rules handle.
//
// Parameters:
// - rulesHandle (IN) - Handle to the rules data structure
//
ANGLE_EXPORT void ANGLEFreeRulesHandle(const RulesHandle rulesHandle);
// The Android EGL loader will call this function in order to free a SystemInfo handle.
//
// Parameters:
// - systemInfoHandle (IN) - Handle to the SystemInfo structure
//
ANGLE_EXPORT void ANGLEFreeSystemInfoHandle(const SystemInfoHandle systemInfoHandle);
// The following are internal versions supported by the current feature-support-utility API.
constexpr unsigned int kFeatureVersion_LowestSupported = 1;
constexpr unsigned int kFeatureVersion_HighestSupported = 2;
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
......
//
// Copyright (c) 2018 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// feature_support_util_unittest.cpp: Unit test for the feature-support utility.
#include <gtest/gtest.h>
#include "../gpu_info_util/SystemInfo.h"
#include "feature_support_util.h"
using namespace angle;
// Test the ANGLEGetFeatureSupportUtilAPIVersion function
TEST(FeatureSupportUtilTest, APIVersion)
{
unsigned int versionToUse;
unsigned int zero = 0;
versionToUse = kFeatureVersion_LowestSupported;
EXPECT_TRUE(ANGLEGetFeatureSupportUtilAPIVersion(&versionToUse));
EXPECT_EQ(kFeatureVersion_LowestSupported, versionToUse);
versionToUse = kFeatureVersion_HighestSupported;
EXPECT_TRUE(ANGLEGetFeatureSupportUtilAPIVersion(&versionToUse));
EXPECT_EQ(kFeatureVersion_HighestSupported, versionToUse);
versionToUse = zero;
EXPECT_FALSE(ANGLEGetFeatureSupportUtilAPIVersion(&versionToUse));
EXPECT_EQ(zero, versionToUse);
versionToUse = kFeatureVersion_HighestSupported + 1;
EXPECT_TRUE(ANGLEGetFeatureSupportUtilAPIVersion(&versionToUse));
EXPECT_EQ(kFeatureVersion_HighestSupported, versionToUse);
}
// Test the ANGLEAddDeviceInfoToSystemInfo function
TEST(FeatureSupportUtilTest, SystemInfo)
{
// TODO(ianelliott): Replace this with a gtest "fixture", per review feedback.
SystemInfo systemInfo;
systemInfo.machineManufacturer = "BAD";
systemInfo.machineModelName = "BAD";
systemInfo.gpus.resize(1);
systemInfo.gpus[0].vendorId = 123;
systemInfo.gpus[0].deviceId = 234;
systemInfo.gpus[0].driverVendor = "DriverVendorA";
systemInfo.gpus[0].detailedDriverVersion = {1, 2, 3, 4};
char mfr[] = "Google";
char model[] = "Pixel1";
ANGLEAddDeviceInfoToSystemInfo(mfr, model, &systemInfo);
EXPECT_EQ("Google", systemInfo.machineManufacturer);
EXPECT_EQ("Pixel1", systemInfo.machineModelName);
}
// Test the ANGLEAndroidParseRulesString function
TEST(FeatureSupportUtilTest, ParseRules)
{
// TODO(ianelliott): Replace this with a gtest "fixture", per review feedback.
SystemInfo systemInfo;
systemInfo.machineManufacturer = "Google";
systemInfo.machineModelName = "Pixel1";
systemInfo.gpus.resize(1);
systemInfo.gpus[0].vendorId = 123;
systemInfo.gpus[0].deviceId = 234;
systemInfo.gpus[0].driverVendor = "DriverVendorA";
systemInfo.gpus[0].detailedDriverVersion = {1, 2, 3, 4};
constexpr char rulesFileContents[] =
"{\"Rules\":[{\"Rule\":\"Default Rule (i.e. use native driver)\", \"AppChoice\":true, "
"\"NonChoice\":false}]}\n";
RulesHandle rulesHandle = nullptr;
int rulesVersion = 0;
EXPECT_TRUE(ANGLEAndroidParseRulesString(rulesFileContents, &rulesHandle, &rulesVersion));
EXPECT_NE(nullptr, rulesHandle);
ANGLEFreeRulesHandle(rulesHandle);
}
...@@ -64,6 +64,9 @@ struct SystemInfo ...@@ -64,6 +64,9 @@ struct SystemInfo
bool isOptimus = false; bool isOptimus = false;
bool isAMDSwitchable = false; bool isAMDSwitchable = false;
// Only available on Android, when added by the feature support utility
std::string machineManufacturer;
// Only available on macOS // Only available on macOS
std::string machineModelName; std::string machineModelName;
std::string machineModelVersion; std::string machineModelVersion;
......
...@@ -113,10 +113,16 @@ test("angle_unittests") { ...@@ -113,10 +113,16 @@ test("angle_unittests") {
sources += [ "angle_unittests_main.cpp" ] sources += [ "angle_unittests_main.cpp" ]
} }
if (is_linux && !is_component_build) {
# Set rpath to find shared libs in a non-component build.
configs += [ "//build/config/gcc:rpath_for_built_shared_libraries" ]
}
deps = googletest_deps + [ deps = googletest_deps + [
angle_root + ":libANGLE", angle_root + ":libANGLE",
angle_root + ":preprocessor", angle_root + ":preprocessor",
angle_root + ":translator", angle_root + ":translator",
angle_root + ":libfeature_support${angle_libs_suffix}",
] ]
} }
......
...@@ -15,6 +15,7 @@ angle_unittests_sources = [ ...@@ -15,6 +15,7 @@ angle_unittests_sources = [
"../common/string_utils_unittest.cpp", "../common/string_utils_unittest.cpp",
"../common/utilities_unittest.cpp", "../common/utilities_unittest.cpp",
"../common/vector_utils_unittest.cpp", "../common/vector_utils_unittest.cpp",
"../feature_support_util/feature_support_util_unittest.cpp",
"../gpu_info_util/SystemInfo_unittest.cpp", "../gpu_info_util/SystemInfo_unittest.cpp",
"../libANGLE/BinaryStream_unittest.cpp", "../libANGLE/BinaryStream_unittest.cpp",
"../libANGLE/BlobCache_unittest.cpp", "../libANGLE/BlobCache_unittest.cpp",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment