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") {
]
}
if (is_android) {
config("angle_feature_support_config") {
include_dirs = [
"include",
"src",
]
defines = [ "LIBFEATURE_SUPPORT_IMPLEMENTATION" ]
if (is_debug) {
defines += [ "ANGLE_FEATURE_UTIL_LOG_VERBOSE" ]
}
config("angle_feature_support_config") {
include_dirs = [
"include",
"src",
]
defines = [ "LIBFEATURE_SUPPORT_IMPLEMENTATION" ]
if (is_debug) {
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_id_config" ]
configs -= angle_undefine_configs
configs += [
":internal_config",
":extra_warnings",
]
public_configs = [
":angle_feature_support_config",
"third_party/jsoncpp:jsoncpp_config",
]
libs = [ "log" ]
}
configs -= angle_undefine_configs
configs += [
":extra_warnings",
":internal_config",
]
sources = [
"src/feature_support_util/angle_feature_support_util.h",
"src/feature_support_util/feature_support_util.cpp",
"src/feature_support_util/feature_support_util.h",
]
deps = [
"third_party/jsoncpp:jsoncpp",
public_configs = [
":angle_feature_support_config",
"third_party/jsoncpp:jsoncpp_config",
]
if (is_mac && !is_component_build) {
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
android_assets("angle_apk_assets") {
disable_compression = true
......
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.internal.cubey"},
{"AppName" : "com.android.gl2jni"}
],
"Devices" :
[
{"Manufacturer" : "Google"
}
{
"Rules":[
{
"Rule":"Default Rule (i.e. use native driver)",
"UseANGLE":false
},
{
"Rule":"Supported application(s) (e.g. Maps on Google devices)",
"UseANGLE":true,
"Applications":[
{
"AppName":"com.google.android.apps.internal.cubey"
},
{
"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 @@
// found in the LICENSE file.
//
// feature_support_util.cpp: Helps Android EGL loader to determine whether to use ANGLE or a native
// GLES driver. Helps ANGLE know which work-arounds to use.
// 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_for_android
//{
#if defined(ANDROID)
# include <android/log.h>
namespace angle
{
// 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
# 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__)
# ifdef ANGLE_FEATURE_UTIL_LOG_VERBOSE
# define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "ANGLE", __VA_ARGS__)
# else
# define ALOGV(...) ((void)0)
# endif
#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 ALOGE(...) printf(__VA_ARGS__);
# define ALOGW(...) printf(__VA_ARGS__);
# define ALOGI(...) printf(__VA_ARGS__);
# define ALOGD(...) printf(__VA_ARGS__);
# define ALOGV(...) printf(__VA_ARGS__);
#define ERR(...) printf(__VA_ARGS__);
#define WARN(...) printf(__VA_ARGS__);
#define INFO(...) printf(__VA_ARGS__);
#define DEBUG(...) printf(__VA_ARGS__);
#define VERBOSE(...) printf(__VA_ARGS__);
#endif // defined(ANDROID)
// JSON values are generally composed of either:
......@@ -47,21 +51,16 @@
// 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.
// 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 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";
// 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 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.
......@@ -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
// 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.
// 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.
......@@ -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
// 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";
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";
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
......@@ -127,9 +123,9 @@ class StringPart
{
public:
StringPart() : mPart(""), mWildcard(true) {}
StringPart(std::string part) : mPart(part), mWildcard(false) {}
StringPart(const std::string part) : mPart(part), mWildcard(false) {}
~StringPart() {}
bool match(StringPart &toCheck)
bool match(const StringPart &toCheck) const
{
return (mWildcard || toCheck.mWildcard || (toCheck.mPart == mPart));
}
......@@ -147,7 +143,7 @@ class IntegerPart
IntegerPart() : mPart(0), mWildcard(true) {}
IntegerPart(uint32_t part) : mPart(part), mWildcard(false) {}
~IntegerPart() {}
bool match(IntegerPart &toCheck)
bool match(const IntegerPart &toCheck) const
{
return (mWildcard || toCheck.mWildcard || (toCheck.mPart == mPart));
}
......@@ -157,32 +153,32 @@ class IntegerPart
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).
// 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(std::string listType) : mListType(listType), mWildcard(true) {}
~ListOf() {}
void addItem(T &toAdd)
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(T &toCheck)
bool match(const T &toCheck) const
{
ALOGV("\t\t Within ListOf<%s> match: wildcards are %s and %s,\n", mListType.c_str(),
mWildcard ? "true" : "false", toCheck.mWildcard ? "true" : "false");
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 (auto &it : mList)
for (const T &it : mList)
{
ALOGV("\t\t Within ListOf<%s> match: calling match on sub-item is %s,\n",
mListType.c_str(), it.match(toCheck) ? "true" : "false");
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;
......@@ -190,17 +186,17 @@ class ListOf
}
return false;
}
T &front() { return (mList.front()); }
void logListOf(std::string prefix, std::string name)
const T &front() const { return (mList.front()); }
void logListOf(const std::string prefix, const std::string name) const
{
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
{
ALOGV("%sListOf%s is has %d item(s):", prefix.c_str(), name.c_str(),
static_cast<int>(mList.size()));
VERBOSE("%sListOf%s has %d item(s):", prefix.c_str(), name.c_str(),
static_cast<int>(mList.size()));
for (auto &it : mList)
{
it.logItem();
......@@ -208,12 +204,11 @@ class ListOf
}
}
bool mWildcard;
private:
std::string mListType;
std::list<T> mList;
public:
bool mWildcard = true;
std::vector<T> mList;
};
// This encapsulates up-to four 32-bit unsigned integers, that represent a potentially-complex
......@@ -240,25 +235,8 @@ class Version
mWildcard(toCopy.mWildcard)
{}
~Version() {}
bool match(Version &toCheck)
{
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)
static Version *CreateVersionFromJson(const Json::Value &jObject)
{
Version *version = nullptr;
// A major version must be provided before a minor, and so on:
......@@ -291,11 +269,28 @@ class Version
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()
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)
{
......@@ -342,100 +337,66 @@ class Version
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.
// 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(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(const std::string name, const Version &version)
: mName(name), mVersion(version), 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(const std::string name) : mName(name), mVersion(), mWildcard(false) {}
Application() : mName(), mVersion(), 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)
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 and intent are optional:
// The application's version is 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);
}
application = new Application(appName, *version);
delete version;
}
else
{
if (jObject.isMember(kJsonIntent) && jObject[kJsonIntent].isString())
{
application = new Application(appName, jObject[kJsonIntent].asString());
}
else
{
application = new Application(appName);
}
application = new Application(appName);
}
// TODO (ianelliott@) (b/113346561) appropriately destruct lists and
// other items that get created from json parsing
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)
{
ALOGV(" Wildcard (i.e. will match all applications)");
VERBOSE(" Wildcard (i.e. will match all applications)");
}
else if (!mVersion.mWildcard)
{
if (!mIntent.mWildcard)
{
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());
VERBOSE(" Application \"%s\" (version: %s)", mName.mPart.c_str(),
mVersion.getString().c_str());
}
else
{
ALOGV(" Application \"%s\"", mName.mPart.c_str());
VERBOSE(" Application \"%s\"", mName.mPart.c_str());
}
}
public:
StringPart mName;
Version mVersion;
StringPart mIntent;
bool mWildcard;
};
......@@ -445,53 +406,49 @@ class Application
class GPU
{
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)
{}
GPU(uint32_t deviceId, Version &version)
: mVendor(), mDeviceId(IntegerPart(deviceId)), mVersion(version), mWildcard(false)
{}
GPU(std::string vendor, uint32_t deviceId)
GPU(const 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(const std::string vendor) : mVendor(vendor), mDeviceId(), mVersion(), mWildcard(false) {}
GPU() : mVendor(), mDeviceId(), mVersion(), mWildcard(true) {}
bool match(GPU &toCheck)
{
ALOGV("\t\t Within GPU match: wildcards are %s and %s,\n", mWildcard ? "true" : "false",
toCheck.mWildcard ? "true" : "false");
ALOGV("\t\t mVendor = \"%s\" and toCheck.mVendor = \"%s\"\n", mVendor.mPart.c_str(),
toCheck.mVendor.mPart.c_str());
ALOGV("\t\t mDeviceId = %d and toCheck.mDeviceId = %d\n", mDeviceId.mPart,
toCheck.mDeviceId.mPart);
ALOGV("\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");
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(Json::Value &jObject)
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())
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 (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);
if (version)
{
gpu = new GPU(vendor, deviceId, *version);
delete version;
}
else
{
......@@ -505,18 +462,17 @@ class GPU
}
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;
}
void logItem()
void logItem() const
{
if (mWildcard)
{
ALOGV(" Wildcard (i.e. will match all GPUs)");
VERBOSE(" Wildcard (i.e. will match all GPUs)");
}
else
{
......@@ -524,18 +480,18 @@ class GPU
{
if (!mVersion.mWildcard)
{
ALOGV("\t GPU vendor: %s, deviceId: 0x%x, version: %s",
mVendor.mPart.c_str(), mDeviceId.mPart, mVersion.getString().c_str());
VERBOSE("\t GPU vendor: %s, deviceId: 0x%x, version: %s",
mVendor.mPart.c_str(), mDeviceId.mPart, mVersion.getString().c_str());
}
else
{
ALOGV("\t GPU vendor: %s, deviceId: 0x%x", mVendor.mPart.c_str(),
mDeviceId.mPart);
VERBOSE("\t GPU vendor: %s, deviceId: 0x%x", mVendor.mPart.c_str(),
mDeviceId.mPart);
}
}
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
class Device
{
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)
{}
Device(std::string manufacturer)
Device(const 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)
{
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)
static Device *CreateDeviceFromJson(const Json::Value &jObject)
{
Device *device = nullptr;
if (jObject.isMember(kJsonManufacturer) && jObject[kJsonManufacturer].isString())
......@@ -601,25 +539,44 @@ class Device
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:
// 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();
}
// TODO (ianelliott@) (b/113346561) appropriately destruct lists and
// other items that get created from json parsing
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 (mGpuList.mWildcard)
{
ALOGV(" Wildcard (i.e. will match all devices)");
VERBOSE(" Wildcard (i.e. will match all devices)");
return;
}
else
{
ALOGV(
VERBOSE(
" Device with any manufacturer and model"
", and with the following GPUs:");
}
......@@ -628,14 +585,14 @@ class Device
{
if (!mModel.mWildcard)
{
ALOGV(
VERBOSE(
" Device manufacturer: \"%s\" and model \"%s\""
", and with the following GPUs:",
mManufacturer.mPart.c_str(), mModel.mPart.c_str());
}
else
{
ALOGV(
VERBOSE(
" Device manufacturer: \"%s\""
", and with the following GPUs:",
mManufacturer.mPart.c_str());
......@@ -652,7 +609,7 @@ class Device
};
// 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
// ANGLE. It is compared with the list of Rules.
class Scenario
......@@ -664,70 +621,53 @@ class Scenario
~Scenario() {}
void logScenario()
{
ALOGV(" Scenario to compare against the rules");
ALOGV(" Application:");
VERBOSE(" Scenario to compare against the rules");
VERBOSE(" Application:");
mApplication.logItem();
ALOGV(" Device:");
VERBOSE(" 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).
// 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(std::string description, bool appChoice, bool answer)
Rule(const std::string description, bool useANGLE)
: mDescription(description),
mAppList("Application"),
mDevList("Device"),
mAppChoice(appChoice),
mAnswer(answer)
mUseANGLE(useANGLE)
{}
~Rule() {}
void addApp(Application &app) { mAppList.addItem(app); }
void addDev(Device &dev) { mDevList.addItem(dev); }
bool match(Scenario &toCheck)
void addApp(const Application &app) { mAppList.addItem(app); }
void addDevice(const Device &dev) { mDevList.addItem(dev); }
bool match(const Scenario &toCheck) const
{
ALOGV(" 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");
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 getAppChoice() { return mAppChoice; }
bool getAnswer() { return mAnswer; }
void logRule()
bool getUseANGLE() const { return mUseANGLE; }
void logRule() const
{
ALOGV(" Rule: \"%s\" %s ANGLE, and %s the app a choice if matched", mDescription.c_str(),
mAnswer ? "enables" : "disables", mAppChoice ? "does give" : "does NOT give");
VERBOSE(" Rule: \"%s\" %s ANGLE", mDescription.c_str(),
mUseANGLE ? "enables" : "disables");
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)
{}
bool mUseANGLE;
};
// This encapsulates a list of Rules that Scenarios are matched against. A Scenario is compared
......@@ -737,178 +677,116 @@ 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;
ALOGV("Checking scenario against %d ANGLE-for-Android rules:",
static_cast<int>(mRuleList.size()));
~RuleList() { mRuleList.clear(); }
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 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)
static RuleList *ReadRulesFromJsonString(const std::string jsonFileContents)
{
RuleList *rules = new RuleList;
// Open the file and start parsing it:
using namespace std;
Json::Reader jReader;
Json::Value jTopLevelObject;
jReader.parse(jsonFileContents, jTopLevelObject);
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();
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);
bool useANGLE = jRule[kJsonUseANGLE].asBool();
Rule *newRule = new Rule(ruleDescription, useANGLE);
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);
// TODO (ianelliott@) (b/113346561) appropriately destruct lists and
// other items that get created from json parsing
newRule->addApp(*newApp);
delete newApp;
}
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);
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);
if (newGPU)
{
newDev->addGPU(*newGPU);
delete newGPU;
}
}
newRule->addDev(*newDev);
newRule->addDevice(*newDev);
delete newDev;
}
// TODO: Need to manage memory
rules->addRule(*newRule);
delete 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);
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()
{
ALOGV("Showing %d ANGLE-for-Android rules:", static_cast<int>(mRuleList.size()));
for (auto &it : mRuleList)
VERBOSE("Showing %d ANGLE-for-Android rules:", static_cast<int>(mRuleList.size()));
for (const Rule &rule : mRuleList)
{
it.logRule();
rule.logRule();
}
}
public:
std::list<Rule> mRuleList;
std::vector<Rule> mRuleList;
};
//} // namespace angle_for_android
} // namespace angle
extern "C" {
// using namespace angle_for_android;
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;
}
using namespace angle;
#if defined(ANGLE_PLATFORM_ANDROID)
// This function is part of the NOW-DEPRECATED version-1 API:
ANGLE_EXPORT bool ANGLEGetUtilityAPI(unsigned int *versionToUse)
{
if (*versionToUse >= kFeatureVersion_LowestSupported)
......@@ -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,
long rules_offset,
long rules_length,
......@@ -947,15 +826,11 @@ ANGLE_EXPORT bool AndroidUseANGLEForApplication(int rules_fd,
// Read the contents of the file into a string and then parse it:
if (rules_fd < 0)
{
ALOGW("Asked to read a non-open JSON file");
WARN("Asked to read a non-open JSON file");
return rtn;
}
off_t fileSize = rules_length;
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);
char *buffer = new char[fileSize + 1];
ssize_t numBytesRead = read(rules_fd, buffer, fileSize);
......@@ -965,11 +840,131 @@ ANGLE_EXPORT bool AndroidUseANGLEForApplication(int rules_fd,
RuleList *rules = RuleList::ReadRulesFromJsonString(jsonFileContents);
rules->logRules();
rtn = rules->getAnswer(scenario);
ALOGV("Application \"%s\" should %s ANGLE", appName, rtn ? "use" : "NOT use");
rtn = rules->getUseANGLE(scenario);
VERBOSE("Application \"%s\" should %s ANGLE", appName, rtn ? "use" : "NOT use");
delete rules;
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"
......@@ -9,47 +9,164 @@
#ifndef FEATURE_SUPPORT_UTIL_H_
#define FEATURE_SUPPORT_UTIL_H_
#include "angle_feature_support_util.h"
#include "export.h"
#ifdef __cplusplus
extern "C" {
#endif
// The following are internal versions supported by the current feature-support-utility API.
constexpr unsigned int kFeatureVersion_LowestSupported = 0;
constexpr unsigned int kFeatureVersion_HighestSupported = 1;
/**************************************************************************************************
*
* The following is the "version 1" private API for the Android EGL loader:
*
**************************************************************************************************/
// The following is the "version 0" external interface that the Android EGL loader used. It is
// deprecated and will soon be obsoleted. It was never declared in the shared header file.
// TODO(ianelliott@google.com angleproject:2801): Revisit this enum. Make it
// strongly typed, and look at renaming it and its values.
typedef enum ANGLEPreference
{
ANGLE_NO_PREFERENCE = 0,
ANGLE_PREFER_NATIVE = 1,
ANGLE_PREFER_ANGLE = 2,
} ANGLEPreference;
// 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
// - 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
// name/interface. Look at generalizing it and making it more "feature"
// oriented.
ANGLE_EXPORT bool ANGLEUseForApplication(const char *appName,
const char *deviceMfr,
const char *deviceModel,
ANGLEPreference developerOption,
ANGLEPreference appPreference);
//
ANGLE_EXPORT bool AndroidUseANGLEForApplication(int rules_fd,
long rules_offset,
long rules_length,
const char *appName,
const char *deviceMfr,
const char *deviceModel);
/**************************************************************************************************
*
* 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
} // 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
bool isOptimus = false;
bool isAMDSwitchable = false;
// Only available on Android, when added by the feature support utility
std::string machineManufacturer;
// Only available on macOS
std::string machineModelName;
std::string machineModelVersion;
......
......@@ -113,10 +113,16 @@ test("angle_unittests") {
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 + [
angle_root + ":libANGLE",
angle_root + ":preprocessor",
angle_root + ":translator",
angle_root + ":libfeature_support${angle_libs_suffix}",
]
}
......
......@@ -15,6 +15,7 @@ angle_unittests_sources = [
"../common/string_utils_unittest.cpp",
"../common/utilities_unittest.cpp",
"../common/vector_utils_unittest.cpp",
"../feature_support_util/feature_support_util_unittest.cpp",
"../gpu_info_util/SystemInfo_unittest.cpp",
"../libANGLE/BinaryStream_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