1d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev/*
2d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev * Copyright (C) 2017 The Android Open Source Project
3d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev *
4d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev * Licensed under the Apache License, Version 2.0 (the "License");
5d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev * you may not use this file except in compliance with the License.
6d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev * You may obtain a copy of the License at
7d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev *
8d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev *      http://www.apache.org/licenses/LICENSE-2.0
9d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev *
10d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev * Unless required by applicable law or agreed to in writing, software
11d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev * distributed under the License is distributed on an "AS IS" BASIS,
12d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev * See the License for the specific language governing permissions and
14d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev * limitations under the License.
15d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev */
16d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
17d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev#include <gtest/gtest.h>
18d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev#include <dirent.h>
19d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev#include <cutils/properties.h>
20d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev#include <cstdint>
21d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev#include <errno.h>
22d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev#include <stdio.h>
23d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev#include <stdlib.h>
24d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev#include <sys/types.h>
25d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev#include <utils/Log.h>
26d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev#include "pipeline/skia/ShaderCache.h"
27d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev#include "FileBlobCache.h"
28d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
29d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Ilievusing namespace android::uirenderer::skiapipeline;
30d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
31d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Ilievnamespace android {
32d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Ilievnamespace uirenderer {
33d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Ilievnamespace skiapipeline {
34d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
35d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Ilievclass ShaderCacheTestUtils {
36d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Ilievpublic:
37d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    /**
38d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev     * "setSaveDelay" sets the time in seconds to wait before saving newly inserted cache entries.
39d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev     * If set to 0, then deferred save is disabled.
40d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev     */
41d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    static void setSaveDelay(ShaderCache& cache, unsigned int saveDelay) {
42d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev        cache.mDeferredSaveDelay = saveDelay;
43d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    }
44d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
45d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    /**
46d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev     * "terminate" optionally stores the BlobCache on disk and release all in-memory cache.
47d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev     * Next call to "initShaderDiskCache" will load again the in-memory cache from disk.
48d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev     */
49d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    static void terminate(ShaderCache& cache, bool saveContent) {
50d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev        std::lock_guard<std::mutex> lock(cache.mMutex);
51d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev        if (cache.mInitialized && cache.mBlobCache && saveContent) {
52d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev            cache.mBlobCache->writeToFile();
53d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev        }
54d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev        cache.mBlobCache = NULL;
55d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    }
56d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev};
57d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
58d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev} /* namespace skiapipeline */
59d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev} /* namespace uirenderer */
60d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev} /* namespace android */
61d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
62d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
63d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Ilievnamespace {
64d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
65d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Ilievstd::string getExternalStorageFolder() {
66d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    return getenv("EXTERNAL_STORAGE");
67d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev}
68d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
69d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Ilievbool folderExist(const std::string& folderName) {
70d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    DIR* dir = opendir(folderName.c_str());
71d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    if (dir) {
72d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev        closedir(dir);
73d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev        return true;
74d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    }
75d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    return false;
76d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev}
77d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
78d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Ilievbool checkShader(const sk_sp<SkData>& shader, const char* program) {
79d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    sk_sp<SkData> shader2 = SkData::MakeWithCString(program);
80d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    return shader->size() == shader2->size()
81d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev            && 0 == memcmp(shader->data(), shader2->data(), shader->size());
82d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev}
83d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
84d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Ilievbool checkShader(const sk_sp<SkData>& shader, std::vector<char>& program) {
85d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    sk_sp<SkData> shader2 = SkData::MakeWithCopy(program.data(), program.size());
86d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    return shader->size() == shader2->size()
87d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev            && 0 == memcmp(shader->data(), shader2->data(), shader->size());
88d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev}
89d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
90d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Ilievvoid setShader(sk_sp<SkData>& shader, const char* program) {
91d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    shader = SkData::MakeWithCString(program);
92d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev}
93d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
94d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Ilievvoid setShader(sk_sp<SkData>& shader, std::vector<char>& program) {
95d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    shader = SkData::MakeWithCopy(program.data(), program.size());
96d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev}
97d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
98d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
99d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
100d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev#define GrProgramDescTest(a) (*SkData::MakeWithCString(#a).get())
101d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
102d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan IlievTEST(ShaderCacheTest, testWriteAndRead) {
103d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    if (!folderExist(getExternalStorageFolder())) {
104d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev        //don't run the test if external storage folder is not available
105d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev        return;
106d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    }
107d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    std::string cacheFile1 =  getExternalStorageFolder() + "/shaderCacheTest1";
108d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    std::string cacheFile2 =  getExternalStorageFolder() + "/shaderCacheTest2";
109d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
110d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    //remove any test files from previous test run
111d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    int deleteFile = remove(cacheFile1.c_str());
112d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
113d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
114d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    //read the cache from a file that does not exist
115d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ShaderCache::get().setFilename(cacheFile1.c_str());
116d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); //disable deferred save
117d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ShaderCache::get().initShaderDiskCache();
118d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
119d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    //read a key - should not be found since the cache is empty
120d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    sk_sp<SkData> outVS;
121d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
122d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
123d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    //write to the in-memory cache without storing on disk and verify we read the same values
124d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    sk_sp<SkData> inVS;
125d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    setShader(inVS, "sassas");
126d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ShaderCache::get().store(GrProgramDescTest(100), *inVS.get());
127d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    setShader(inVS, "someVS");
128d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
129d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(100))), sk_sp<SkData>());
130d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ASSERT_TRUE(checkShader(outVS, "sassas"));
131d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
132d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ASSERT_TRUE(checkShader(outVS, "someVS"));
133d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
134d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    //store content to disk and release in-memory cache
135d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
136d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
137d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    //change to a file that does not exist and verify load fails
138d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ShaderCache::get().setFilename(cacheFile2.c_str());
139d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ShaderCache::get().initShaderDiskCache();
140d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
141d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
142d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
143d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    //load again content from disk from an existing file and check the data is read correctly
144d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ShaderCache::get().setFilename(cacheFile1.c_str());
145d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ShaderCache::get().initShaderDiskCache();
146d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    sk_sp<SkData> outVS2;
147d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
148d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ASSERT_TRUE(checkShader(outVS2, "someVS"));
149d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
150d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    //change data, store to disk, read back again and verify data has been changed
151d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    setShader(inVS, "ewData1");
152d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
153d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
154d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ShaderCache::get().initShaderDiskCache();
155d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
156d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ASSERT_TRUE(checkShader(outVS2, "ewData1"));
157d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
158d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
159d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    //write and read big data chunk (50K)
160d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    size_t dataSize = 50*1024;
161d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    std::vector<char> dataBuffer(dataSize);
162d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    for (size_t i = 0; i < dataSize; i++) {
163d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev        dataBuffer[0] = dataSize % 256;
164d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    }
165d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    setShader(inVS, dataBuffer);
166d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
167d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
168d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ShaderCache::get().initShaderDiskCache();
169d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
170d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ASSERT_TRUE(checkShader(outVS2, dataBuffer));
171d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
172d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
173d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev    remove(cacheFile1.c_str());
174d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev}
175d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev
176d495f43992c98d04cb5a4b1a7bf7917154072fb8Stan Iliev}  // namespace
177