1 /*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include <mutex>
9
10 #include <android/asset_manager.h>
11 #include <android/asset_manager_jni.h>
12 #include <jni.h>
13 #include <sys/stat.h>
14
15 #include "include/core/SkStream.h"
16 #include "include/private/SkTo.h"
17 #include "tools/ResourceFactory.h"
18
19 #include "tools/skqp/src/skqp.h"
20
21 ////////////////////////////////////////////////////////////////////////////////
22 extern "C" {
23 JNIEXPORT void JNICALL Java_org_skia_skqp_SkQP_nInit(JNIEnv*, jobject, jobject, jstring);
24 JNIEXPORT jlong JNICALL Java_org_skia_skqp_SkQP_nExecuteGM(JNIEnv*, jobject, jint, jint);
25 JNIEXPORT jobjectArray JNICALL Java_org_skia_skqp_SkQP_nExecuteUnitTest(JNIEnv*, jobject, jint);
26 JNIEXPORT void JNICALL Java_org_skia_skqp_SkQP_nMakeReport(JNIEnv*, jobject);
27 } // extern "C"
28 ////////////////////////////////////////////////////////////////////////////////
29
30 static AAssetManager* gAAssetManager = nullptr;
31
open_asset_data(const char * path)32 static sk_sp<SkData> open_asset_data(const char* path) {
33 sk_sp<SkData> data;
34 if (gAAssetManager) {
35 if (AAsset* asset = AAssetManager_open(gAAssetManager, path, AASSET_MODE_STREAMING)) {
36 if (size_t size = SkToSizeT(AAsset_getLength(asset))) {
37 data = SkData::MakeUninitialized(size);
38 int ret = AAsset_read(asset, data->writable_data(), size);
39 if (ret != SkToInt(size)) {
40 SkDebugf("ERROR: AAsset_read != AAsset_getLength (%s)\n", path);
41 }
42 }
43 AAsset_close(asset);
44 }
45 }
46 return data;
47 }
48
49 namespace {
50 struct AndroidAssetManager : public SkQPAssetManager {
open__anonb94184e40111::AndroidAssetManager51 sk_sp<SkData> open(const char* path) override { return open_asset_data(path); }
52 };
53 }
54
55 // TODO(halcanary): Should not have global variables; SkQP Java object should
56 // own pointers and manage concurency.
57 static AndroidAssetManager gAndroidAssetManager;
58 static std::mutex gMutex;
59 static SkQP gSkQP;
60
61 #define jassert(env, cond, ret) do { if (!(cond)) { \
62 (env)->ThrowNew((env)->FindClass("java/lang/Exception"), \
63 __FILE__ ": assert(" #cond ") failed."); \
64 return ret; } } while (0)
65
set_string_array_element(JNIEnv * env,jobjectArray a,const char * s,unsigned i)66 static void set_string_array_element(JNIEnv* env, jobjectArray a, const char* s, unsigned i) {
67 jstring jstr = env->NewStringUTF(s);
68 jassert(env, jstr != nullptr,);
69 env->SetObjectArrayElement(a, (jsize)i, jstr);
70 env->DeleteLocalRef(jstr);
71 }
72
73 ////////////////////////////////////////////////////////////////////////////////
74
get_resource(const char * resource)75 sk_sp<SkData> get_resource(const char* resource) {
76 return open_asset_data((std::string("resources/") + resource).c_str());
77 }
78
79 ////////////////////////////////////////////////////////////////////////////////
80
81 template <typename T, typename F>
to_java_string_array(JNIEnv * env,const std::vector<T> & array,F toString)82 jobjectArray to_java_string_array(JNIEnv* env,
83 const std::vector<T>& array,
84 F toString) {
85 jclass stringClass = env->FindClass("java/lang/String");
86 jassert(env, stringClass, nullptr);
87 jobjectArray jarray = env->NewObjectArray((jint)array.size(), stringClass, nullptr);
88 jassert(env, jarray != nullptr, nullptr);
89 for (unsigned i = 0; i < array.size(); ++i) {
90 set_string_array_element(env, jarray, std::string(toString(array[i])).c_str(), i);
91 }
92 return jarray;
93 }
94
to_string(JNIEnv * env,jstring jString)95 static std::string to_string(JNIEnv* env, jstring jString) {
96 const char* utf8String = env->GetStringUTFChars(jString, nullptr);
97 jassert(env, utf8String && utf8String[0], "");
98 std::string sString(utf8String);
99 env->ReleaseStringUTFChars(jString, utf8String);
100 return sString;
101 }
102
Java_org_skia_skqp_SkQP_nInit(JNIEnv * env,jobject object,jobject assetManager,jstring dataDir)103 void Java_org_skia_skqp_SkQP_nInit(JNIEnv* env, jobject object, jobject assetManager,
104 jstring dataDir) {
105 jclass SkQP_class = env->GetObjectClass(object);
106
107 // tools/Resources
108 gResourceFactory = &get_resource;
109
110 std::string reportDirectory = to_string(env, dataDir);
111
112 jassert(env, assetManager,);
113 // This global must be set before using AndroidAssetManager
114 gAAssetManager = AAssetManager_fromJava(env, assetManager);
115 jassert(env, gAAssetManager,);
116
117 std::lock_guard<std::mutex> lock(gMutex);
118 gSkQP.init(&gAndroidAssetManager, nullptr, reportDirectory.c_str());
119
120 auto backends = gSkQP.getSupportedBackends();
121 jassert(env, backends.size() > 0,);
122 auto gms = gSkQP.getGMs();
123 jassert(env, gms.size() > 0,);
124 auto unitTests = gSkQP.getUnitTests();
125 jassert(env, unitTests.size() > 0,);
126
127 constexpr char kStringArrayType[] = "[Ljava/lang/String;";
128 env->SetObjectField(object, env->GetFieldID(SkQP_class, "mBackends", kStringArrayType),
129 to_java_string_array(env, backends, SkQP::GetBackendName));
130 env->SetObjectField(object, env->GetFieldID(SkQP_class, "mUnitTests", kStringArrayType),
131 to_java_string_array(env, unitTests, SkQP::GetUnitTestName));
132 env->SetObjectField(object, env->GetFieldID(SkQP_class, "mGMs", kStringArrayType),
133 to_java_string_array(env, gms, SkQP::GetGMName));
134 }
135
Java_org_skia_skqp_SkQP_nExecuteGM(JNIEnv * env,jobject object,jint gmIndex,jint backendIndex)136 jlong Java_org_skia_skqp_SkQP_nExecuteGM(JNIEnv* env,
137 jobject object,
138 jint gmIndex,
139 jint backendIndex) {
140 SkQP::RenderOutcome outcome;
141 std::string except;
142 {
143 std::lock_guard<std::mutex> lock(gMutex);
144 jassert(env, backendIndex < (jint)gSkQP.getSupportedBackends().size(), -1);
145 jassert(env, gmIndex < (jint)gSkQP.getGMs().size(), -1);
146 SkQP::SkiaBackend backend = gSkQP.getSupportedBackends()[backendIndex];
147 SkQP::GMFactory gm = gSkQP.getGMs()[gmIndex];
148 std::tie(outcome, except) = gSkQP.evaluateGM(backend, gm);
149 }
150
151 if (!except.empty()) {
152 (void)env->ThrowNew(env->FindClass("org/skia/skqp/SkQPException"), except.c_str());
153 }
154 return (jlong)outcome.fTotalError;
155 }
156
Java_org_skia_skqp_SkQP_nExecuteUnitTest(JNIEnv * env,jobject object,jint index)157 jobjectArray Java_org_skia_skqp_SkQP_nExecuteUnitTest(JNIEnv* env,
158 jobject object,
159 jint index) {
160 std::vector<std::string> errors;
161 {
162 jassert(env, index < (jint)gSkQP.getUnitTests().size(), nullptr);
163 std::lock_guard<std::mutex> lock(gMutex);
164 errors = gSkQP.executeTest(gSkQP.getUnitTests()[index]);
165 }
166 if (errors.size() == 0) {
167 return nullptr;
168 }
169 jclass stringClass = env->FindClass("java/lang/String");
170 jassert(env, stringClass, nullptr);
171 jobjectArray array = env->NewObjectArray(errors.size(), stringClass, nullptr);
172 for (unsigned i = 0; i < errors.size(); ++i) {
173 set_string_array_element(env, array, errors[i].c_str(), i);
174 }
175 return (jobjectArray)env->NewGlobalRef(array);
176 }
177
Java_org_skia_skqp_SkQP_nMakeReport(JNIEnv *,jobject)178 void Java_org_skia_skqp_SkQP_nMakeReport(JNIEnv*, jobject) {
179 std::lock_guard<std::mutex> lock(gMutex);
180 gSkQP.makeReport();
181 }
182
183 ////////////////////////////////////////////////////////////////////////////////
184
185