Commit 4d42ef39 by Ian Elliott Committed by Commit Bot

Add A4A opt-in/out logic

This version of the code #ifdef's out the JSON-parsing code (which dependency was not yet found). Instead, 2 in-code rules are created and used (default and run Maps). Bug: angleproject:2794 Change-Id: I9d8c00459d5e674035f4073ccb839fac8d4d7aa5 Reviewed-on: https://chromium-review.googlesource.com/1200375 Commit-Queue: Ian Elliott <ianelliott@google.com> Reviewed-by: 's avatarIan Elliott <ianelliott@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 7ada46c9
...@@ -232,22 +232,28 @@ config("angle_feature_support_util_config") { ...@@ -232,22 +232,28 @@ config("angle_feature_support_util_config") {
} }
static_library("angle_feature_support_util") { static_library("angle_feature_support_util") {
sources = [
"src/feature_support_util/feature_support_util.cpp",
"src/feature_support_util/feature_support_util.h",
]
configs -= angle_undefine_configs configs -= angle_undefine_configs
configs += [ configs += [
":internal_config", ":internal_config",
":extra_warnings", ":extra_warnings",
] ]
public_configs = [ ":angle_feature_support_util_config" ] public_configs = [
":angle_feature_support_util_config",
"third_party/jsoncpp:jsoncpp_config",
]
public_deps = [ public_deps = [
":angle_common", ":angle_common",
] ]
sources = [
"src/feature_support_util/feature_support_util.cpp",
"src/feature_support_util/feature_support_util.h",
]
deps = [
"third_party/jsoncpp:jsoncpp",
]
} }
config("angle_gpu_info_util_config") { config("angle_gpu_info_util_config") {
...@@ -493,13 +499,15 @@ static_library("libANGLE") { ...@@ -493,13 +499,15 @@ static_library("libANGLE") {
":angle_common", ":angle_common",
] ]
deps = [ deps = [
":angle_feature_support_util",
":angle_gpu_info_util", ":angle_gpu_info_util",
":angle_image_util", ":angle_image_util",
":commit_id", ":commit_id",
":includes", ":includes",
":translator", ":translator",
] ]
if (is_android) {
deps += [ ":angle_feature_support_util" ]
}
# Shared D3D sources. # Shared D3D sources.
if (angle_enable_d3d9 || angle_enable_d3d11) { if (angle_enable_d3d9 || angle_enable_d3d11) {
......
R"=====({"Rules":
[
{"Rule" : "Default Rule (i.e. use native driver)",
"AppChoice" : true, "NonChoice" : false
},
{"Rule" : "Supported application(s) (e.g. Maps on Google devices)",
"AppChoice" : true, "NonChoice" : true,
"Applications" :
[
{"AppName" : "com.google.android.apps.maps"}
],
"Devices" :
[
{"Manufacturer" : "Google"
}
]
}
]
}
)====="
...@@ -8,10 +8,891 @@ ...@@ -8,10 +8,891 @@
// determine whether to use ANGLE or a native GLES driver. // 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 <string.h>
#include <fstream>
#include <list>
// namespace angle_for_android
//{
#if defined(ANDROID)
#include <android/log.h>
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, "ANGLE", __VA_ARGS__)
#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, "ANGLE", __VA_ARGS__)
#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, "ANGLE", __VA_ARGS__)
#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "ANGLE", __VA_ARGS__)
#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "ANGLE", __VA_ARGS__)
#else // defined(ANDROID)
#define ALOGE(...) printf(__VA_ARGS__);
#define ALOGW(...) printf(__VA_ARGS__);
#define ALOGI(...) printf(__VA_ARGS__);
#define ALOGD(...) printf(__VA_ARGS__);
#define ALOGV(...) printf(__VA_ARGS__);
#endif // defined(ANDROID)
// JSON values are generally composed of either:
// - Objects, which are a set of comma-separated string:value pairs (note the recursive nature)
// - Arrays, which are a set of comma-separated values.
// We'll call the string in a string:value pair the "identifier". These identifiers are defined
// below, as follows:
// 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
// matches, the rule's version of the answer (true or false) becomes the new answer. After all
// rules are processed, the most-recent answer is the final answer.
constexpr char kJsonRules[] = "Rules";
// 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:
constexpr char kJsonRule[] = "Rule";
// Within a rule, the JSON identifier for one type of answer--whether to allow an application to
// specify whether to use ANGLE. The value is a boolean (i.e. true or false), with true allowing
// the application to specify whether or not to use ANGLE.
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
// array of objects, each object of which can specify attributes of an application.
constexpr char kJsonApplications[] = "Applications";
// Within an object that describes the attributes of an application, the JSON identifier for the
// 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.
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
// array of objects, each object of which can specify attributes of a device.
constexpr char kJsonDevices[] = "Devices";
// 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,
// this must be the first attribute specified in the object.
constexpr char kJsonManufacturer[] = "Manufacturer";
// Within an object that describes the attributes of a device, the JSON identifier for the
// model of the device. The value is a string.
constexpr char kJsonModel[] = "Model";
// Within an object that describes the attributes of a device, the JSON identifier for describing
// one or more GPUs/drivers used in the device. The value is an
// array of objects, each object of which can specify attributes of a GPU and its driver.
constexpr char kJsonGPUs[] = "GPUs";
// 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,
// this must be the first attribute specified in the object.
constexpr char kJsonvendor[] = "vendor";
// 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
// specified, this must preceded the version attributes specified in the object.
constexpr char kJsondeviceId[] = "deviceId";
// 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
// integer number. Not specifying a major version implies a wildcard for all values of a version.
constexpr char kJsonVerMajor[] = "VerMajor";
// Within an object that describes the attributes of either an application or a GPU, the JSON
// identifier for the minor version of that application or GPU driver. The value is a positive
// integer number. In order to specify a minor version, it must be specified immediately after the
// major number associated with it. Not specifying a minor version implies a wildcard for the
// minor, subminor, and patch values of a version.
constexpr char kJsonVerMinor[] = "VerMinor";
// Within an object that describes the attributes of either an application or a GPU, the JSON
// identifier for the subminor version of that application or GPU driver. The value is a positive
// integer number. In order to specify a subminor version, it must be specified immediately after
// the minor number associated with it. Not specifying a subminor version implies a wildcard for
// the subminor and patch values of a version.
constexpr char kJsonVerSubMinor[] = "VerSubMinor";
// Within an object that describes the attributes of either an application or a GPU, the JSON
// identifier for the patch version of that application or GPU driver. The value is a positive
// integer number. In order to specify a patch version, it must be specified immediately after the
// subminor number associated with it. Not specifying a patch version implies a wildcard for the
// patch value of a version.
constexpr char kJsonVerPatch[] = "VerPatch";
// This encapsulates a std::string. The default constructor (not given a string) assumes that this
// is a wildcard (i.e. will match all other StringPart objects).
class StringPart
{
public:
StringPart() : mPart(""), mWildcard(true) {}
StringPart(std::string part) : mPart(part), mWildcard(false) {}
~StringPart() {}
bool match(StringPart &toCheck)
{
return (mWildcard || toCheck.mWildcard || (toCheck.mPart == mPart));
}
public:
std::string mPart;
bool mWildcard;
};
// This encapsulates a 32-bit unsigned integer. The default constructor (not given a number)
// assumes that this is a wildcard (i.e. will match all other IntegerPart objects).
class IntegerPart
{
public:
IntegerPart() : mPart(0), mWildcard(true) {}
IntegerPart(uint32_t part) : mPart(part), mWildcard(false) {}
~IntegerPart() {}
bool match(IntegerPart &toCheck)
{
return (mWildcard || toCheck.mWildcard || (toCheck.mPart == mPart));
}
public:
uint32_t mPart;
bool mWildcard;
};
// This encapsulates a list of other classes, which of which will have a match() method. The
// common constructor (given a type, but not any list items) assumes that this is a wildcard
// (i.e. will match all other ListOf<t> objects).
template <class T>
class ListOf
{
public:
ListOf(std::string listType) : mListType(listType), mWildcard(true) {}
~ListOf() {}
void addItem(T &toAdd)
{
mList.push_back(toAdd);
mWildcard = false;
}
bool match(T &toCheck)
{
ALOGD("\t\t Within ListOf<%s> match: wildcards are %s and %s,\n", mListType.c_str(),
mWildcard ? "true" : "false", toCheck.mWildcard ? "true" : "false");
if (mWildcard || toCheck.mWildcard)
{
return true;
}
for (auto &it : mList)
{
ALOGD("\t\t Within ListOf<%s> match: calling match on sub-item is %s,\n",
mListType.c_str(), it.match(toCheck) ? "true" : "false");
if (it.match(toCheck))
{
return true;
}
}
return false;
}
T &front() { return (mList.front()); }
void logListOf(std::string prefix, std::string name)
{
if (mWildcard)
{
ALOGD("%sListOf%s is wildcarded to always match", prefix.c_str(), name.c_str());
}
else
{
int nItems = mList.size();
ALOGD("%sListOf%s is has %d item(s):", prefix.c_str(), name.c_str(), nItems);
for (auto &it : mList)
{
it.logItem();
}
}
}
private:
std::string mListType;
std::list<T> mList;
public:
bool mWildcard = true;
};
// This encapsulates up-to four 32-bit unsigned integers, that represent a potentially-complex
// version number. The default constructor (not given any numbers) assumes that this is a wildcard
// (i.e. will match all other Version objects). Each part of a Version is stored in an IntegerPart
// class, and so may be wildcarded as well.
class Version
{
public:
Version(uint32_t major, uint32_t minor, uint32_t subminor, uint32_t patch)
: mMajor(major), mMinor(minor), mSubminor(subminor), mPatch(patch), mWildcard(false)
{
}
Version(uint32_t major, uint32_t minor, uint32_t subminor)
: mMajor(major), mMinor(minor), mSubminor(subminor), mWildcard(false)
{
}
Version(uint32_t major, uint32_t minor) : mMajor(major), mMinor(minor), mWildcard(false) {}
Version(uint32_t major) : mMajor(major), mWildcard(false) {}
Version() : mWildcard(true) {}
Version(const Version &toCopy)
: mMajor(toCopy.mMajor),
mMinor(toCopy.mMinor),
mSubminor(toCopy.mSubminor),
mPatch(toCopy.mPatch),
mWildcard(toCopy.mWildcard)
{
}
~Version() {}
bool match(Version &toCheck)
{
ALOGD("\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))
{
ALOGD("\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;
// A major version must be provided before a minor, and so on:
if (jObject.isMember(kJsonVerMajor) && jObject[kJsonVerMajor].isInt())
{
int major = jObject[kJsonVerMajor].asInt();
if (jObject.isMember(kJsonVerMinor) && jObject[kJsonVerMinor].isInt())
{
int minor = jObject[kJsonVerMinor].asInt();
if (jObject.isMember(kJsonVerSubMinor) && jObject[kJsonVerSubMinor].isInt())
{
int subMinor = jObject[kJsonVerSubMinor].asInt();
if (jObject.isMember(kJsonVerPatch) && jObject[kJsonVerPatch].isInt())
{
int patch = jObject[kJsonVerPatch].asInt();
version = new Version(major, minor, subMinor, patch);
}
else
{
version = new Version(major, minor, subMinor);
}
}
else
{
version = new Version(major, minor);
}
}
else
{
version = new Version(major);
}
}
// TODO (ianelliott@) (b/113346561) appropriately destruct lists and
// other items that get created from json parsing
return version;
}
std::string getString()
{
if (mWildcard)
{
return "*";
}
else
{
char ret[100];
// Must at least have a major version:
if (!mMinor.mWildcard)
{
if (!mSubminor.mWildcard)
{
if (!mPatch.mWildcard)
{
snprintf(ret, 100, "%d.%d.%d.%d", mMajor.mPart, mMinor.mPart,
mSubminor.mPart, mPatch.mPart);
}
else
{
snprintf(ret, 100, "%d.%d.%d.*", mMajor.mPart, mMinor.mPart,
mSubminor.mPart);
}
}
else
{
snprintf(ret, 100, "%d.%d.*", mMajor.mPart, mMinor.mPart);
}
}
else
{
snprintf(ret, 100, "%d.*", mMajor.mPart);
}
std::string retString = ret;
return retString;
}
}
public:
IntegerPart mMajor;
IntegerPart mMinor;
IntegerPart mSubminor;
IntegerPart mPatch;
bool mWildcard;
};
// This encapsulates an application, and potentially the application's Version and/or the intent
// that it is launched with. The default constructor (not given any values) assumes that this is a
// wildcard (i.e. will match all other Application objects). Each part of an Application is stored
// in a class that may also be wildcarded.
class Application
{
public:
Application(std::string name, Version &version, std::string intent)
: mName(name), mVersion(version), mIntent(intent), mWildcard(false)
{
}
Application(std::string name, std::string intent)
: mName(name), mVersion(), mIntent(intent), mWildcard(false)
{
}
Application(std::string name, Version &version)
: mName(name), mVersion(version), mIntent(), mWildcard(false)
{
}
Application(std::string name) : mName(name), mVersion(), mIntent(), mWildcard(false) {}
Application() : mName(), mVersion(), mIntent(), mWildcard(true) {}
~Application() {}
bool match(Application &toCheck)
{
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;
// If an application is listed, the application's name is required:
std::string appName = jObject[kJsonAppName].asString();
// The application's version and intent are optional:
Version *version = Version::CreateVersionFromJson(jObject);
if (version)
{
if (jObject.isMember(kJsonIntent) && jObject[kJsonIntent].isString())
{
application = new Application(appName, *version, jObject[kJsonIntent].asString());
}
else
{
application = new Application(appName, *version);
}
}
else
{
if (jObject.isMember(kJsonIntent) && jObject[kJsonIntent].isString())
{
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;
}
void logItem()
{
if (mWildcard)
{
ALOGD(" Wildcard (i.e. will match all applications)");
}
else if (!mVersion.mWildcard)
{
if (!mIntent.mWildcard)
{
ALOGD(" Application \"%s\" (version: %s; intent: \"%s\")", mName.mPart.c_str(),
mVersion.getString().c_str(), mIntent.mPart.c_str());
}
else
{
ALOGD(" Application \"%s\" (version: %s)", mName.mPart.c_str(),
mVersion.getString().c_str());
}
}
else if (!mIntent.mWildcard)
{
ALOGD(" Application \"%s\" (intent: \"%s\")", mName.mPart.c_str(),
mIntent.mPart.c_str());
}
else
{
ALOGD(" Application \"%s\"", mName.mPart.c_str());
}
}
public:
StringPart mName;
Version mVersion;
StringPart mIntent;
bool mWildcard;
};
// This encapsulates a GPU and its driver. The default constructor (not given any values) assumes
// that this is a wildcard (i.e. will match all other GPU objects). Each part of a GPU is stored
// in a class that may also be wildcarded.
class GPU
{
public:
GPU(std::string vendor, uint32_t deviceId, Version &version)
: mVendor(vendor), mDeviceId(IntegerPart(deviceId)), mVersion(version), mWildcard(false)
{
}
GPU(uint32_t deviceId, Version &version)
: mVendor(), mDeviceId(IntegerPart(deviceId)), mVersion(version), mWildcard(false)
{
}
GPU(std::string vendor, uint32_t deviceId)
: mVendor(vendor), mDeviceId(IntegerPart(deviceId)), mVersion(), mWildcard(false)
{
}
GPU(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) {}
bool match(GPU &toCheck)
{
ALOGD("\t\t Within GPU match: wildcards are %s and %s,\n", mWildcard ? "true" : "false",
toCheck.mWildcard ? "true" : "false");
ALOGD("\t\t mVendor = \"%s\" and toCheck.mVendor = \"%s\"\n", mVendor.mPart.c_str(),
toCheck.mVendor.mPart.c_str());
ALOGD("\t\t mDeviceId = %d and toCheck.mDeviceId = %d\n", mDeviceId.mPart,
toCheck.mDeviceId.mPart);
ALOGD("\t\t mVendor match is %s, mDeviceId is %s, mVersion is %s\n",
toCheck.mVendor.match(mVendor) ? "true" : "false",
toCheck.mDeviceId.match(mDeviceId) ? "true" : "false",
toCheck.mVersion.match(mVersion) ? "true" : "false");
return (mWildcard || toCheck.mWildcard ||
(toCheck.mVendor.match(mVendor) && toCheck.mDeviceId.match(mDeviceId) &&
toCheck.mVersion.match(mVersion)));
}
~GPU() {}
static GPU *CreateGpuFromJson(Json::Value &jObject)
{
GPU *gpu = nullptr;
// If a GPU is listed, the vendor name is required:
if (jObject.isMember(kJsonvendor) && jObject[kJsonvendor].isString())
{
std::string vendor = jObject[kJsonvendor].asString();
// If a version is given, the deviceId is required:
if (jObject.isMember(kJsondeviceId) && jObject[kJsondeviceId].isUInt())
{
uint32_t deviceId = jObject[kJsondeviceId].asUInt();
Version *version = Version::CreateVersionFromJson(jObject);
if (version)
{
gpu = new GPU(vendor, deviceId, *version);
}
else
{
gpu = new GPU(vendor, deviceId);
}
}
else
{
gpu = new GPU(vendor);
}
}
else
{
ALOGD("Asked to parse a GPU, but no GPU found");
}
// TODO (ianelliott@) (b/113346561) appropriately destruct lists and
// other items that get created from json parsing
return gpu;
}
void logItem()
{
if (mWildcard)
{
ALOGD(" Wildcard (i.e. will match all GPUs)");
}
else
{
if (!mDeviceId.mWildcard)
{
if (!mVersion.mWildcard)
{
ALOGD("\t GPU vendor: %s, deviceId: 0x%x, version: %s",
mVendor.mPart.c_str(), mDeviceId.mPart, mVersion.getString().c_str());
}
else
{
ALOGD("\t GPU vendor: %s, deviceId: 0x%x", mVendor.mPart.c_str(),
mDeviceId.mPart);
}
}
else
{
ALOGD("\t GPU vendor: %s", mVendor.mPart.c_str());
}
}
}
public:
StringPart mVendor;
IntegerPart mDeviceId;
Version mVersion;
bool mWildcard;
};
// This encapsulates a device, and potentially the device's model and/or a list of GPUs/drivers
// associated with the Device. The default constructor (not given any values) assumes that this is
// a wildcard (i.e. will match all other Device objects). Each part of a Device is stored in a
// class that may also be wildcarded.
class Device
{
public:
Device(std::string manufacturer, std::string model)
: mManufacturer(manufacturer), mModel(model), mGpuList("GPU"), mWildcard(false)
{
}
Device(std::string manufacturer)
: mManufacturer(manufacturer), mModel(), mGpuList("GPU"), mWildcard(false)
{
}
Device() : mManufacturer(), mModel(), mGpuList("GPU"), mWildcard(true) {}
~Device() {}
void addGPU(GPU &gpu) { mGpuList.addItem(gpu); }
bool match(Device &toCheck)
{
ALOGD("\t Within Device match: wildcards are %s and %s,\n", mWildcard ? "true" : "false",
toCheck.mWildcard ? "true" : "false");
if (!(mWildcard || toCheck.mWildcard))
{
ALOGD("\t Manufacturer match is %s, model is %s\n",
toCheck.mManufacturer.match(mManufacturer) ? "true" : "false",
toCheck.mModel.match(mModel) ? "true" : "false");
}
ALOGD("\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;
if (jObject.isMember(kJsonManufacturer) && jObject[kJsonManufacturer].isString())
{
std::string manufacturerName = jObject[kJsonManufacturer].asString();
// We don't let a model be specified without also specifying an Manufacturer:
if (jObject.isMember(kJsonModel) && jObject[kJsonModel].isString())
{
std::string model = jObject[kJsonModel].asString();
device = new Device(manufacturerName, model);
}
else
{
device = new Device(manufacturerName);
}
}
else
{
// 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:
device = new Device();
}
// TODO (ianelliott@) (b/113346561) appropriately destruct lists and
// other items that get created from json parsing
return device;
}
void logItem()
{
if (mWildcard)
{
if (mGpuList.mWildcard)
{
ALOGD(" Wildcard (i.e. will match all devices)");
return;
}
else
{
ALOGD(
" Device with any manufacturer and model"
", and with the following GPUs:");
}
}
else
{
if (!mModel.mWildcard)
{
ALOGD(
" Device manufacturer: \"%s\" and model \"%s\""
", and with the following GPUs:",
mManufacturer.mPart.c_str(), mModel.mPart.c_str());
}
else
{
ALOGD(
" Device manufacturer: \"%s\""
", and with the following GPUs:",
mManufacturer.mPart.c_str());
}
}
mGpuList.logListOf(" ", "GPUs");
}
public:
StringPart mManufacturer;
StringPart mModel;
ListOf<GPU> mGpuList;
bool mWildcard;
};
// 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
// 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.
class Scenario
{
public:
Scenario(const char *appName, const char *deviceMfr, const char *deviceModel)
: mApplication(Application(appName)), mDevice(Device(deviceMfr, deviceModel))
{
}
~Scenario() {}
void logScenario()
{
ALOGD(" Scenario to compare against the rules");
ALOGD(" Application:");
mApplication.logItem();
ALOGD(" Device:");
mDevice.logItem();
}
public:
Application mApplication;
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
// the Rule. A Rule always has answers, but can potentially wildcard every item in it (i.e. match
// every scenario).
class Rule
{
public:
Rule(std::string description, bool appChoice, bool answer)
: mDescription(description),
mAppList("Application"),
mDevList("Device"),
mAppChoice(appChoice),
mAnswer(answer)
{
}
~Rule() {}
void addApp(Application &app) { mAppList.addItem(app); }
void addDev(Device &dev) { mDevList.addItem(dev); }
bool match(Scenario &toCheck)
{
ALOGD(" Within \"%s\" Rule: application match is %s and device match is %s\n",
mDescription.c_str(), mAppList.match(toCheck.mApplication) ? "true" : "false",
mDevList.match(toCheck.mDevice) ? "true" : "false");
return (mAppList.match(toCheck.mApplication) && mDevList.match(toCheck.mDevice));
}
bool getAppChoice() { return mAppChoice; }
bool getAnswer() { return mAnswer; }
void logRule()
{
ALOGD(" Rule: \"%s\" %s ANGLE, and %s the app a choice if matched", mDescription.c_str(),
mAnswer ? "enables" : "disables", mAppChoice ? "does give" : "does NOT give");
mAppList.logListOf(" ", "Applications");
mDevList.logListOf(" ", "Devices");
}
public:
std::string mDescription;
ListOf<Application> mAppList;
ListOf<Device> mDevList;
bool mAppChoice;
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
// with each Rule, in order. Any time a Scenario matches a Rule, the current answer is overridden
// with the answer of the matched Rule.
class RuleList
{
public:
RuleList() {}
~RuleList() {}
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;
int nRules = mRuleList.size();
ALOGD("Checking scenario against %d ANGLE-for-Android rules:", nRules);
for (auto &it : mRuleList)
{
ALOGD(" Checking Rule: \"%s\" (to see whether there's a match)",
it.mDescription.c_str());
if (it.match(toCheck))
{
ALOGD(" -> Rule matches. Setting the app choice to %s",
it.getAppChoice() ? "true" : "false");
appChoice = it.getAppChoice();
}
else
{
ALOGD(" -> 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;
int nRules = mRuleList.size();
ALOGD("Checking scenario against %d ANGLE-for-Android rules:", nRules);
for (auto &it : mRuleList)
{
ALOGD(" Checking Rule: \"%s\" (to see whether there's a match)",
it.mDescription.c_str());
if (it.match(toCheck))
{
ALOGD(" -> Rule matches. Setting the answer to %s",
it.getAnswer() ? "true" : "false");
answer = it.getAnswer();
}
else
{
ALOGD(" -> Rule doesn't match.");
}
}
return answer;
}
static RuleList *ReadRulesFromJsonFile()
{
RuleList *rules = new RuleList;
// Open the file and start parsing it:
using namespace std;
#ifdef READ_FROM_JSON_FILE
// FIXME/TODO: NEED TO GET THE FINAL LOCATION AND ENSURE THAT ANY APPLICATION CAN READ FROM
// THAT LOCATION.
ifstream ifs("/system/app/ANGLEPrebuilt/a4a_rules.json");
Json::Reader jReader;
Json::Value jTopLevelObject;
jReader.parse(ifs, jTopLevelObject);
#else // READ_FROM_JSON_FILE
// Embed the rules file contents into a string:
const char *s =
#include "a4a_rules.json"
;
std::string jsonFileContents = s;
Json::Reader jReader;
Json::Value jTopLevelObject;
jReader.parse(jsonFileContents, jTopLevelObject);
#endif // READ_FROM_JSON_FILE
Json::Value jRules = jTopLevelObject[kJsonRules];
for (unsigned int i = 0; i < jRules.size(); i++)
{
Json::Value jRule = jRules[i];
std::string ruleDescription = jRule[kJsonRule].asString();
bool ruleAppChoice = jRule[kJsonAppChoice].asBool();
bool ruleAnswer = jRule[kJsonNonChoice].asBool();
// 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];
for (unsigned int j = 0; j < jApps.size(); j++)
{
Json::Value jApp = jApps[j];
Application *newApp = Application::CreateApplicationFromJson(jApp);
// TODO (ianelliott@) (b/113346561) appropriately destruct lists and
// other items that get created from json parsing
newRule->addApp(*newApp);
}
Json::Value jDevs = jRule[kJsonDevices];
for (unsigned int j = 0; j < jDevs.size(); j++)
{
Json::Value jDev = jDevs[j];
Device *newDev = Device::CreateDeviceFromJson(jDev);
Json::Value jGPUs = jDev[kJsonGPUs];
for (unsigned int k = 0; k < jGPUs.size(); k++)
{
Json::Value jGPU = jGPUs[k];
GPU *newGPU = GPU::CreateGpuFromJson(jGPU);
if (newGPU)
{
newDev->addGPU(*newGPU);
}
}
newRule->addDev(*newDev);
}
// TODO: Need to manage memory
rules->addRule(*newRule);
}
// Make sure there is at least one, default rule. If not, add it here:
int nRules = rules->mRuleList.size();
if (nRules == 0)
{
Rule defaultRule("Default Rule", true, false);
rules->addRule(defaultRule);
}
return rules;
}
void logRules()
{
int nRules = mRuleList.size();
ALOGD("Showing %d ANGLE-for-Android rules:", nRules);
for (auto &it : mRuleList)
{
it.logRule();
}
}
public:
std::list<Rule> mRuleList;
};
//} // namespace angle_for_android
#ifdef __cplusplus
extern "C" { extern "C" {
#endif
// using namespace angle_for_android;
ANGLE_EXPORT bool ANGLEUseForApplication(const char *appName, ANGLE_EXPORT bool ANGLEUseForApplication(const char *appName,
const char *deviceMfr, const char *deviceMfr,
...@@ -19,20 +900,24 @@ ANGLE_EXPORT bool ANGLEUseForApplication(const char *appName, ...@@ -19,20 +900,24 @@ ANGLE_EXPORT bool ANGLEUseForApplication(const char *appName,
ANGLEPreference developerOption, ANGLEPreference developerOption,
ANGLEPreference appPreference) ANGLEPreference appPreference)
{ {
Scenario scenario(appName, deviceMfr, deviceModel);
RuleList *rules = RuleList::ReadRulesFromJsonFile();
scenario.logScenario();
rules->logRules();
if (developerOption != ANGLE_NO_PREFERENCE) if (developerOption != ANGLE_NO_PREFERENCE)
{ {
return (developerOption == ANGLE_PREFER_ANGLE); return (developerOption == ANGLE_PREFER_ANGLE);
} }
else if ((appPreference != ANGLE_NO_PREFERENCE) && false /*rules allow app to choose*/) else if ((appPreference != ANGLE_NO_PREFERENCE) && rules->getAppChoice(scenario))
{ {
return (appPreference == ANGLE_PREFER_ANGLE); return (appPreference == ANGLE_PREFER_ANGLE);
} }
else else
{ {
return false /*whatever the rules come up with*/; return rules->getAnswer(scenario);
} }
delete rules;
} }
#ifdef __cplusplus
} // extern "C" } // extern "C"
#endif
...@@ -24,6 +24,16 @@ typedef enum ANGLEPreference { ...@@ -24,6 +24,16 @@ typedef enum ANGLEPreference {
ANGLE_PREFER_ANGLE = 2, ANGLE_PREFER_ANGLE = 2,
} ANGLEPreference; } ANGLEPreference;
// 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:
// - 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
// - developerOption - Whether the "Developer Options" setting was set, and if so, how
// - appPreference - Whether the application expressed a preference, and if so, how
//
// TODO(ianelliott@google.com angleproject:2801): Revisit this function // TODO(ianelliott@google.com angleproject:2801): Revisit this function
// name/interface. Look at generalizing it and making it more "feature" // name/interface. Look at generalizing it and making it more "feature"
// oriented. // oriented.
......
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