blob: 1433aa0349f479731445f09347109b2602b373c5 [file] [log] [blame]
Stan Ilievd495f432017-10-09 15:49:32 -04001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <gtest/gtest.h>
18#include <dirent.h>
19#include <cutils/properties.h>
20#include <cstdint>
21#include <errno.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <sys/types.h>
25#include <utils/Log.h>
26#include "pipeline/skia/ShaderCache.h"
27#include "FileBlobCache.h"
28
29using namespace android::uirenderer::skiapipeline;
30
31namespace android {
32namespace uirenderer {
33namespace skiapipeline {
34
35class ShaderCacheTestUtils {
36public:
37 /**
38 * "setSaveDelay" sets the time in seconds to wait before saving newly inserted cache entries.
39 * If set to 0, then deferred save is disabled.
40 */
41 static void setSaveDelay(ShaderCache& cache, unsigned int saveDelay) {
42 cache.mDeferredSaveDelay = saveDelay;
43 }
44
45 /**
46 * "terminate" optionally stores the BlobCache on disk and release all in-memory cache.
47 * Next call to "initShaderDiskCache" will load again the in-memory cache from disk.
48 */
49 static void terminate(ShaderCache& cache, bool saveContent) {
50 std::lock_guard<std::mutex> lock(cache.mMutex);
Yichi Chen9f959552018-03-29 21:21:54 +080051 cache.mSavePending = saveContent;
52 cache.saveToDiskLocked();
Stan Ilievd495f432017-10-09 15:49:32 -040053 cache.mBlobCache = NULL;
54 }
Yichi Chen9f959552018-03-29 21:21:54 +080055
56 /**
57 *
58 */
59 template <typename T>
60 static bool validateCache(ShaderCache& cache, std::vector<T> hash) {
61 return cache.validateCache(hash.data(), hash.size() * sizeof(T));
62 }
Stan Ilievd495f432017-10-09 15:49:32 -040063};
64
65} /* namespace skiapipeline */
66} /* namespace uirenderer */
67} /* namespace android */
68
69
70namespace {
71
72std::string getExternalStorageFolder() {
73 return getenv("EXTERNAL_STORAGE");
74}
75
76bool folderExist(const std::string& folderName) {
77 DIR* dir = opendir(folderName.c_str());
78 if (dir) {
79 closedir(dir);
80 return true;
81 }
82 return false;
83}
84
Yichi Chen9f959552018-03-29 21:21:54 +080085inline bool
86checkShader(const sk_sp<SkData>& shader1, const sk_sp<SkData>& shader2) {
87 return nullptr != shader1 && nullptr != shader2 && shader1->size() == shader2->size()
88 && 0 == memcmp(shader1->data(), shader2->data(), shader1->size());
Stan Ilievd495f432017-10-09 15:49:32 -040089}
90
Yichi Chen9f959552018-03-29 21:21:54 +080091inline bool
92checkShader(const sk_sp<SkData>& shader, const char* program) {
93 sk_sp<SkData> shader2 = SkData::MakeWithCString(program);
94 return checkShader(shader, shader2);
95}
96
97template <typename T>
98bool checkShader(const sk_sp<SkData>& shader, std::vector<T>& program) {
99 sk_sp<SkData> shader2 = SkData::MakeWithCopy(program.data(), program.size() * sizeof(T));
100 return checkShader(shader, shader2);
Stan Ilievd495f432017-10-09 15:49:32 -0400101}
102
103void setShader(sk_sp<SkData>& shader, const char* program) {
104 shader = SkData::MakeWithCString(program);
105}
106
Yichi Chen9f959552018-03-29 21:21:54 +0800107template <typename T>
108void setShader(sk_sp<SkData>& shader, std::vector<T>& buffer) {
109 shader = SkData::MakeWithCopy(buffer.data(), buffer.size() * sizeof(T));
Stan Ilievd495f432017-10-09 15:49:32 -0400110}
111
Yichi Chen9f959552018-03-29 21:21:54 +0800112template <typename T>
113void genRandomData(std::vector<T>& buffer) {
114 for (auto& data : buffer) {
115 data = T(std::rand());
116 }
117}
Stan Ilievd495f432017-10-09 15:49:32 -0400118
119
120#define GrProgramDescTest(a) (*SkData::MakeWithCString(#a).get())
121
122TEST(ShaderCacheTest, testWriteAndRead) {
123 if (!folderExist(getExternalStorageFolder())) {
124 //don't run the test if external storage folder is not available
125 return;
126 }
127 std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
128 std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
129
130 //remove any test files from previous test run
131 int deleteFile = remove(cacheFile1.c_str());
132 ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
Yichi Chen9f959552018-03-29 21:21:54 +0800133 std::srand(0);
Stan Ilievd495f432017-10-09 15:49:32 -0400134
135 //read the cache from a file that does not exist
136 ShaderCache::get().setFilename(cacheFile1.c_str());
137 ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); //disable deferred save
138 ShaderCache::get().initShaderDiskCache();
139
140 //read a key - should not be found since the cache is empty
141 sk_sp<SkData> outVS;
142 ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
143
144 //write to the in-memory cache without storing on disk and verify we read the same values
145 sk_sp<SkData> inVS;
146 setShader(inVS, "sassas");
147 ShaderCache::get().store(GrProgramDescTest(100), *inVS.get());
148 setShader(inVS, "someVS");
149 ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
150 ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(100))), sk_sp<SkData>());
151 ASSERT_TRUE(checkShader(outVS, "sassas"));
152 ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
153 ASSERT_TRUE(checkShader(outVS, "someVS"));
154
155 //store content to disk and release in-memory cache
156 ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
157
158 //change to a file that does not exist and verify load fails
159 ShaderCache::get().setFilename(cacheFile2.c_str());
160 ShaderCache::get().initShaderDiskCache();
161 ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
162 ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
163
164 //load again content from disk from an existing file and check the data is read correctly
165 ShaderCache::get().setFilename(cacheFile1.c_str());
166 ShaderCache::get().initShaderDiskCache();
167 sk_sp<SkData> outVS2;
168 ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
169 ASSERT_TRUE(checkShader(outVS2, "someVS"));
170
171 //change data, store to disk, read back again and verify data has been changed
172 setShader(inVS, "ewData1");
173 ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
174 ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
175 ShaderCache::get().initShaderDiskCache();
176 ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
177 ASSERT_TRUE(checkShader(outVS2, "ewData1"));
178
179
180 //write and read big data chunk (50K)
181 size_t dataSize = 50*1024;
Yichi Chen9f959552018-03-29 21:21:54 +0800182 std::vector<uint8_t> dataBuffer(dataSize);
183 genRandomData(dataBuffer);
Stan Ilievd495f432017-10-09 15:49:32 -0400184 setShader(inVS, dataBuffer);
185 ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
186 ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
187 ShaderCache::get().initShaderDiskCache();
188 ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
189 ASSERT_TRUE(checkShader(outVS2, dataBuffer));
190
191 ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
192 remove(cacheFile1.c_str());
193}
194
Yichi Chen9f959552018-03-29 21:21:54 +0800195TEST(ShaderCacheTest, testCacheValidation) {
196 if (!folderExist(getExternalStorageFolder())) {
197 //don't run the test if external storage folder is not available
198 return;
199 }
200 std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
201 std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
202
203 //remove any test files from previous test run
204 int deleteFile = remove(cacheFile1.c_str());
205 ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
206 std::srand(0);
207
208 //generate identity and read the cache from a file that does not exist
209 ShaderCache::get().setFilename(cacheFile1.c_str());
210 ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); //disable deferred save
211 std::vector<uint8_t> identity(1024);
212 genRandomData(identity);
213 ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
214 sizeof(decltype(identity)::value_type));
215
216 // generate random content in cache and store to disk
217 constexpr size_t numBlob(10);
218 constexpr size_t keySize(1024);
219 constexpr size_t dataSize(50 * 1024);
220
221 std::vector< std::pair<sk_sp<SkData>, sk_sp<SkData>> > blobVec(numBlob);
222 for (auto& blob : blobVec) {
223 std::vector<uint8_t> keyBuffer(keySize);
224 std::vector<uint8_t> dataBuffer(dataSize);
225 genRandomData(keyBuffer);
226 genRandomData(dataBuffer);
227
228 sk_sp<SkData> key, data;
229 setShader(key, keyBuffer);
230 setShader(data, dataBuffer);
231
232 blob = std::make_pair(key, data);
233 ShaderCache::get().store(*key.get(), *data.get());
234 }
235 ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
236
237 // change to a file that does not exist and verify validation fails
238 ShaderCache::get().setFilename(cacheFile2.c_str());
239 ShaderCache::get().initShaderDiskCache();
240 ASSERT_FALSE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
241 ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
242
243 // restore the original file and verify validation succeeds
244 ShaderCache::get().setFilename(cacheFile1.c_str());
245 ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
246 sizeof(decltype(identity)::value_type));
247 ASSERT_TRUE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
248 for (const auto& blob : blobVec) {
249 auto outVS = ShaderCache::get().load(*blob.first.get());
250 ASSERT_TRUE( checkShader(outVS, blob.second) );
251 }
252
253 // generate error identity and verify load fails
254 ShaderCache::get().initShaderDiskCache(identity.data(), -1);
255 for (const auto& blob : blobVec) {
256 ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
257 }
258 ShaderCache::get().initShaderDiskCache(nullptr, identity.size() *
259 sizeof(decltype(identity)::value_type));
260 for (const auto& blob : blobVec) {
261 ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
262 }
263
264 // verify the cache validation again after load fails
265 ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
266 sizeof(decltype(identity)::value_type));
267 ASSERT_TRUE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
268 for (const auto& blob : blobVec) {
269 auto outVS = ShaderCache::get().load(*blob.first.get());
270 ASSERT_TRUE( checkShader(outVS, blob.second) );
271 }
272
273 // generate another identity and verify load fails
274 for (auto& data : identity) {
275 data += std::rand();
276 }
277 ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
278 sizeof(decltype(identity)::value_type));
279 for (const auto& blob : blobVec) {
280 ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
281 }
282
283 ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
284 remove(cacheFile1.c_str());
285}
286
Stan Ilievd495f432017-10-09 15:49:32 -0400287} // namespace