• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "ResourceFactory.h"
16 #include "SkStream.h"
17 #include "SkTo.h"
18 
19 #include "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__anona4a03d860111::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, 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