blob: 00700c52c6de933a244a4c12e8aaad0c8c37b982 [file] [log] [blame]
//
// 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.
//
// feature_support_util.cpp: Helps client APIs make decisions based on rules
// 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 <json/json.h>
#include <string.h>
#include "common/platform.h"
#if defined(ANGLE_PLATFORM_ANDROID)
# include <android/log.h>
# include <unistd.h>
#endif
#include <fstream>
#include <list>
#include "../gpu_info_util/SystemInfo.h"
namespace angle
{
#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
# define ERR(...) __android_log_print(ANDROID_LOG_ERROR, "ANGLE", __VA_ARGS__)
# define WARN(...) __android_log_print(ANDROID_LOG_WARN, "ANGLE", __VA_ARGS__)
# define INFO(...) __android_log_print(ANDROID_LOG_INFO, "ANGLE", __VA_ARGS__)
# define DEBUG(...) __android_log_print(ANDROID_LOG_DEBUG, "ANGLE", __VA_ARGS__)
# ifdef ANGLE_FEATURE_UTIL_LOG_VERBOSE
# define VERBOSE(...) __android_log_print(ANDROID_LOG_VERBOSE, "ANGLE", __VA_ARGS__)
# else
# define VERBOSE(...) ((void)0)
# endif
#else // defined(ANDROID)
# define ERR(...) printf(__VA_ARGS__)
# define WARN(...) printf(__VA_ARGS__)
# define INFO(...) printf(__VA_ARGS__)
# define DEBUG(...) printf(__VA_ARGS__)
// Uncomment for debugging.
//# define VERBOSE(...) printf(__VA_ARGS__)
# define VERBOSE(...)
#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. 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 the value is a user-firendly description of the rule:
constexpr char kJsonRule[] = "Rule";
// Within a rule, the JSON identifier for the answer--whether or not to use ANGLE. The value is a
// boolean (i.e. true or false).
constexpr char kJsonUseANGLE[] = "UseANGLE";
// 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 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 non-GPU 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(const std::string part) : mPart(part), mWildcard(false) {}
~StringPart() {}
bool match(const StringPart &toCheck) const
{
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(const IntegerPart &toCheck) const
{
return (mWildcard || toCheck.mWildcard || (toCheck.mPart == mPart));
}
public:
uint32_t mPart;
bool mWildcard;
};
// This encapsulates a list of other classes, each of which will have a match() and logItem()
// 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(const std::string listType) : mWildcard(true), mListType(listType) {}
~ListOf() { mList.clear(); }
void addItem(const T &toAdd)
{
mList.push_back(toAdd);
mWildcard = false;
}
bool match(const T &toCheck) const
{
VERBOSE("\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 (const T &it : mList)
{
VERBOSE("\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;
}
bool match(const ListOf<T> &toCheck) const
{
VERBOSE("\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;
}
// If we make it to here, both this and toCheck have at least one item in their mList
for (const T &it : toCheck.mList)
{
if (match(it))
{
return true;
}
}
return false;
}
void logListOf(const std::string prefix, const std::string name) const
{
if (mWildcard)
{
VERBOSE("%sListOf%s is wildcarded to always match", prefix.c_str(), name.c_str());
}
else
{
VERBOSE("%sListOf%s has %d item(s):", prefix.c_str(), name.c_str(),
static_cast<int>(mList.size()));
for (auto &it : mList)
{
it.logItem();
}
}
}
bool mWildcard;
private:
std::string mListType;
std::vector<T> mList;
};
// 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() {}
static Version *CreateVersionFromJson(const 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);
}
}
return version;
}
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)
{
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. 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(const std::string name, const Version &version)
: mName(name), mVersion(version), mWildcard(false)
{}
Application(const std::string name) : mName(name), mVersion(), mWildcard(false) {}
Application() : mName(), mVersion(), mWildcard(true) {}
~Application() {}
static Application *CreateApplicationFromJson(const 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 is optional:
Version *version = Version::CreateVersionFromJson(jObject);
if (version)
{
application = new Application(appName, *version);
delete version;
}
else
{
application = new Application(appName);
}
return application;
}
bool match(const Application &toCheck) const
{
return (mWildcard || toCheck.mWildcard ||
(toCheck.mName.match(mName) && toCheck.mVersion.match(mVersion)));
}
void logItem() const
{
if (mWildcard)
{
VERBOSE(" Wildcard (i.e. will match all applications)");
}
else if (!mVersion.mWildcard)
{
VERBOSE(" Application \"%s\" (version: %s)", mName.mPart.c_str(),
mVersion.getString().c_str());
}
else
{
VERBOSE(" Application \"%s\"", mName.mPart.c_str());
}
}
public:
StringPart mName;
Version mVersion;
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(const std::string vendor, uint32_t deviceId, const Version &version)
: mVendor(vendor), mDeviceId(IntegerPart(deviceId)), mVersion(version), mWildcard(false)
{}
GPU(const std::string vendor, uint32_t deviceId)
: mVendor(vendor), mDeviceId(IntegerPart(deviceId)), mVersion(), mWildcard(false)
{}
GPU(const std::string vendor) : mVendor(vendor), mDeviceId(), mVersion(), mWildcard(false) {}
GPU() : mVendor(), mDeviceId(), mVersion(), mWildcard(true) {}
bool match(const GPU &toCheck) const
{
VERBOSE("\t\t Within GPU match: wildcards are %s and %s,\n", mWildcard ? "true" : "false",
toCheck.mWildcard ? "true" : "false");
VERBOSE("\t\t mVendor = \"%s\" and toCheck.mVendor = \"%s\"\n", mVendor.mPart.c_str(),
toCheck.mVendor.mPart.c_str());
VERBOSE("\t\t mDeviceId = %d and toCheck.mDeviceId = %d\n", mDeviceId.mPart,
toCheck.mDeviceId.mPart);
VERBOSE("\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(const 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);
delete version;
}
else
{
gpu = new GPU(vendor, deviceId);
}
}
else
{
gpu = new GPU(vendor);
}
}
else
{
WARN("Asked to parse a GPU, but no vendor found");
}
return gpu;
}
void logItem() const
{
if (mWildcard)
{
VERBOSE(" Wildcard (i.e. will match all GPUs)");
}
else
{
if (!mDeviceId.mWildcard)
{
if (!mVersion.mWildcard)
{
VERBOSE("\t GPU vendor: %s, deviceId: 0x%x, version: %s",
mVendor.mPart.c_str(), mDeviceId.mPart, mVersion.getString().c_str());
}
else
{
VERBOSE("\t GPU vendor: %s, deviceId: 0x%x", mVendor.mPart.c_str(),
mDeviceId.mPart);
}
}
else
{
VERBOSE("\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(const std::string manufacturer, const std::string model)
: mManufacturer(manufacturer), mModel(model), mGpuList("GPU"), mWildcard(false)
{}
Device(const std::string manufacturer)
: mManufacturer(manufacturer), mModel(), mGpuList("GPU"), mWildcard(false)
{}
Device() : mManufacturer(), mModel(), mGpuList("GPU"), mWildcard(true) {}
~Device() {}
static Device *CreateDeviceFromJson(const 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, 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();
}
return device;
}
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))) &&
mGpuList.match(toCheck.mGpuList));
}
void logItem() const
{
if (mWildcard)
{
if (mGpuList.mWildcard)
{
VERBOSE(" Wildcard (i.e. will match all devices)");
return;
}
else
{
VERBOSE(
" Device with any manufacturer and model"
", and with the following GPUs:");
}
}
else
{
if (!mModel.mWildcard)
{
VERBOSE(
" Device manufacturer: \"%s\" and model \"%s\""
", and with the following GPUs:",
mManufacturer.mPart.c_str(), mModel.mPart.c_str());
}
else
{
VERBOSE(
" 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 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
// 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()
{
VERBOSE(" Scenario to compare against the rules");
VERBOSE(" Application:");
mApplication.logItem();
VERBOSE(" Device:");
mDevice.logItem();
}
public:
Application mApplication;
Device mDevice;
};
// This encapsulates a Rule that provides an answer based on whether a particular Scenario matches
// the Rule. A Rule always has an answer, but can potentially wildcard every item in it (i.e.
// match every scenario).
class Rule
{
public:
Rule(const std::string description, bool useANGLE)
: mDescription(description),
mAppList("Application"),
mDevList("Device"),
mUseANGLE(useANGLE)
{}
~Rule() {}
void addApp(const Application &app) { mAppList.addItem(app); }
void addDevice(const Device &dev) { mDevList.addItem(dev); }
bool match(const Scenario &toCheck) const
{
VERBOSE(" 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 getUseANGLE() const { return mUseANGLE; }
void logRule() const
{
VERBOSE(" Rule: \"%s\" %s ANGLE", mDescription.c_str(),
mUseANGLE ? "enables" : "disables");
mAppList.logListOf(" ", "Applications");
mDevList.logListOf(" ", "Devices");
}
std::string mDescription;
ListOf<Application> mAppList;
ListOf<Device> mDevList;
bool mUseANGLE;
};
// 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() { mRuleList.clear(); }
static RuleList *ReadRulesFromJsonString(const std::string jsonFileContents)
{
RuleList *rules = new RuleList;
// Open the file and start parsing it:
Json::Reader jReader;
Json::Value jTopLevelObject;
jReader.parse(jsonFileContents, jTopLevelObject);
Json::Value jRules = jTopLevelObject[kJsonRules];
for (unsigned int ruleIndex = 0; ruleIndex < jRules.size(); ruleIndex++)
{
Json::Value jRule = jRules[ruleIndex];
std::string ruleDescription = jRule[kJsonRule].asString();
bool useANGLE = jRule[kJsonUseANGLE].asBool();
Rule *newRule = new Rule(ruleDescription, useANGLE);
Json::Value jApps = jRule[kJsonApplications];
for (unsigned int appIndex = 0; appIndex < jApps.size(); appIndex++)
{
Json::Value jApp = jApps[appIndex];
Application *newApp = Application::CreateApplicationFromJson(jApp);
newRule->addApp(*newApp);
delete newApp;
}
Json::Value jDevs = jRule[kJsonDevices];
for (unsigned int deviceIndex = 0; deviceIndex < jDevs.size(); deviceIndex++)
{
Json::Value jDev = jDevs[deviceIndex];
Device *newDev = Device::CreateDeviceFromJson(jDev);
Json::Value jGPUs = jDev[kJsonGPUs];
for (unsigned int gpuIndex = 0; gpuIndex < jGPUs.size(); gpuIndex++)
{
Json::Value jGPU = jGPUs[gpuIndex];
GPU *newGPU = GPU::CreateGpuFromJson(jGPU);
if (newGPU)
{
newDev->addGPU(*newGPU);
delete newGPU;
}
}
newRule->addDevice(*newDev);
delete newDev;
}
rules->addRule(*newRule);
delete newRule;
}
// Make sure there is at least one, default rule. If not, add it here:
if (rules->mRuleList.size() == 0)
{
Rule defaultRule("Default Rule", false);
rules->addRule(defaultRule);
}
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()
{
VERBOSE("Showing %d ANGLE-for-Android rules:", static_cast<int>(mRuleList.size()));
for (const Rule &rule : mRuleList)
{
rule.logRule();
}
}
public:
std::vector<Rule> mRuleList;
};
} // namespace angle
extern "C" {
using namespace angle;
// 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;
}
// TODO (http://anglebug.com/3036): Restore the real code
angle::SystemInfo *systemInfo = new angle::SystemInfo;
systemInfo->gpus.resize(1);
GPUDeviceInfo &gpu = systemInfo->gpus[0];
gpu.vendorId = 0xFEFEFEFE;
gpu.deviceId = 0xFEEEFEEE;
gpu.driverVendor = "Foo";
gpu.driverVersion = "1.2.3.4";
*systemInfoHandle = systemInfo;
return true;
}
// 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"