Implement SkSL cache
Implement SkSL cache by reusing code and logic from egl_cache_t. Test: Improves startup times for gmail by 15ms and 10ms for calc app. Bug: 66740665 Change-Id: I9ba479c649ba97a2c29a48d40579ba001264c957
This commit is contained in:
@@ -70,6 +70,7 @@ public final class ThreadedRenderer {
|
||||
* Name of the file that holds the shaders cache.
|
||||
*/
|
||||
private static final String CACHE_PATH_SHADERS = "com.android.opengl.shaders_cache";
|
||||
private static final String CACHE_PATH_SKIASHADERS = "com.android.skia.shaders_cache";
|
||||
|
||||
/**
|
||||
* System property used to enable or disable threaded rendering profiling.
|
||||
@@ -272,7 +273,9 @@ public final class ThreadedRenderer {
|
||||
* @hide
|
||||
*/
|
||||
public static void setupDiskCache(File cacheDir) {
|
||||
ThreadedRenderer.setupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath());
|
||||
ThreadedRenderer.setupShadersDiskCache(
|
||||
new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath(),
|
||||
new File(cacheDir, CACHE_PATH_SKIASHADERS).getAbsolutePath());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1007,7 +1010,7 @@ public final class ThreadedRenderer {
|
||||
/** Not actually public - internal use only. This doc to make lint happy */
|
||||
public static native void disableVsync();
|
||||
|
||||
static native void setupShadersDiskCache(String cacheFile);
|
||||
static native void setupShadersDiskCache(String cacheFile, String skiaCacheFile);
|
||||
|
||||
private static native void nRotateProcessStatsBuffer();
|
||||
private static native void nSetProcessStatsBuffer(int fd);
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
#include <renderthread/RenderProxy.h>
|
||||
#include <renderthread/RenderTask.h>
|
||||
#include <renderthread/RenderThread.h>
|
||||
#include <pipeline/skia/ShaderCache.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
@@ -970,10 +971,14 @@ static void android_view_ThreadedRenderer_removeFrameMetricsObserver(JNIEnv* env
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz,
|
||||
jstring diskCachePath) {
|
||||
jstring diskCachePath, jstring skiaDiskCachePath) {
|
||||
const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL);
|
||||
android::egl_set_cache_filename(cacheArray);
|
||||
env->ReleaseStringUTFChars(diskCachePath, cacheArray);
|
||||
|
||||
const char* skiaCacheArray = env->GetStringUTFChars(skiaDiskCachePath, NULL);
|
||||
uirenderer::skiapipeline::ShaderCache::get().setFilename(skiaCacheArray);
|
||||
env->ReleaseStringUTFChars(skiaDiskCachePath, skiaCacheArray);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -1018,7 +1023,7 @@ static const JNINativeMethod gMethods[] = {
|
||||
{ "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending },
|
||||
{ "nSerializeDisplayListTree", "(J)V", (void*) android_view_ThreadedRenderer_serializeDisplayListTree },
|
||||
{ "nDumpProfileInfo", "(JLjava/io/FileDescriptor;I)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo },
|
||||
{ "setupShadersDiskCache", "(Ljava/lang/String;)V",
|
||||
{ "setupShadersDiskCache", "(Ljava/lang/String;Ljava/lang/String;)V",
|
||||
(void*) android_view_ThreadedRenderer_setupShadersDiskCache },
|
||||
{ "nAddRenderNode", "(JJZ)V", (void*) android_view_ThreadedRenderer_addRenderNode},
|
||||
{ "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode},
|
||||
|
||||
@@ -68,6 +68,7 @@ cc_defaults {
|
||||
],
|
||||
static_libs: [
|
||||
"libplatformprotos",
|
||||
"libEGL_blobCache",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -131,6 +132,7 @@ cc_defaults {
|
||||
"pipeline/skia/LayerDrawable.cpp",
|
||||
"pipeline/skia/RenderNodeDrawable.cpp",
|
||||
"pipeline/skia/ReorderBarrierDrawables.cpp",
|
||||
"pipeline/skia/ShaderCache.cpp",
|
||||
"pipeline/skia/SkiaDisplayList.cpp",
|
||||
"pipeline/skia/SkiaOpenGLPipeline.cpp",
|
||||
"pipeline/skia/SkiaOpenGLReadback.cpp",
|
||||
@@ -346,6 +348,7 @@ cc_test {
|
||||
"tests/unit/RecordingCanvasTests.cpp",
|
||||
"tests/unit/RenderNodeTests.cpp",
|
||||
"tests/unit/RenderPropertiesTests.cpp",
|
||||
"tests/unit/ShaderCacheTests.cpp",
|
||||
"tests/unit/SkiaBehaviorTests.cpp",
|
||||
"tests/unit/SkiaDisplayListTests.cpp",
|
||||
"tests/unit/SkiaPipelineTests.cpp",
|
||||
|
||||
139
libs/hwui/pipeline/skia/ShaderCache.cpp
Normal file
139
libs/hwui/pipeline/skia/ShaderCache.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ShaderCache.h"
|
||||
#include <algorithm>
|
||||
#include <log/log.h>
|
||||
#include <thread>
|
||||
#include "FileBlobCache.h"
|
||||
#include "utils/TraceUtils.h"
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
namespace skiapipeline {
|
||||
|
||||
// Cache size limits.
|
||||
static const size_t maxKeySize = 1024;
|
||||
static const size_t maxValueSize = 64 * 1024;
|
||||
static const size_t maxTotalSize = 512 * 1024;
|
||||
|
||||
ShaderCache::ShaderCache() {
|
||||
// There is an "incomplete FileBlobCache type" compilation error, if ctor is moved to header.
|
||||
}
|
||||
|
||||
ShaderCache ShaderCache::sCache;
|
||||
|
||||
ShaderCache& ShaderCache::get() {
|
||||
return sCache;
|
||||
}
|
||||
|
||||
void ShaderCache::initShaderDiskCache() {
|
||||
ATRACE_NAME("initShaderDiskCache");
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
if (mFilename.length() > 0) {
|
||||
mBlobCache.reset(new FileBlobCache(maxKeySize, maxValueSize, maxTotalSize, mFilename));
|
||||
mInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderCache::setFilename(const char* filename) {
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
mFilename = filename;
|
||||
}
|
||||
|
||||
BlobCache* ShaderCache::getBlobCacheLocked() {
|
||||
LOG_ALWAYS_FATAL_IF(!mInitialized, "ShaderCache has not been initialized");
|
||||
return mBlobCache.get();
|
||||
}
|
||||
|
||||
sk_sp<SkData> ShaderCache::load(const SkData& key) {
|
||||
ATRACE_NAME("ShaderCache::load");
|
||||
size_t keySize = key.size();
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
if (!mInitialized) {
|
||||
ALOGE("ShaderCache::load not initialized");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// mObservedBlobValueSize is reasonably big to avoid memory reallocation
|
||||
// Allocate a buffer with malloc. SkData takes ownership of that allocation and will call free.
|
||||
void* valueBuffer = malloc(mObservedBlobValueSize);
|
||||
if (!valueBuffer) {
|
||||
return nullptr;
|
||||
}
|
||||
BlobCache* bc = getBlobCacheLocked();
|
||||
size_t valueSize = bc->get(key.data(), keySize, valueBuffer, mObservedBlobValueSize);
|
||||
int maxTries = 3;
|
||||
while (valueSize > mObservedBlobValueSize && maxTries > 0) {
|
||||
mObservedBlobValueSize = std::min(valueSize, maxValueSize);
|
||||
valueBuffer = realloc(valueBuffer, mObservedBlobValueSize);
|
||||
if (!valueBuffer) {
|
||||
return nullptr;
|
||||
}
|
||||
valueSize = bc->get(key.data(), keySize, valueBuffer, mObservedBlobValueSize);
|
||||
maxTries--;
|
||||
}
|
||||
if (!valueSize) {
|
||||
free(valueBuffer);
|
||||
return nullptr;
|
||||
}
|
||||
if (valueSize > mObservedBlobValueSize) {
|
||||
ALOGE("ShaderCache::load value size is too big %d", (int) valueSize);
|
||||
free(valueBuffer);
|
||||
return nullptr;
|
||||
}
|
||||
return SkData::MakeFromMalloc(valueBuffer, valueSize);
|
||||
}
|
||||
|
||||
void ShaderCache::store(const SkData& key, const SkData& data) {
|
||||
ATRACE_NAME("ShaderCache::store");
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
|
||||
if (!mInitialized) {
|
||||
ALOGE("ShaderCache::store not initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t valueSize = data.size();
|
||||
size_t keySize = key.size();
|
||||
if (keySize == 0 || valueSize == 0 || valueSize >= maxValueSize) {
|
||||
ALOGW("ShaderCache::store: sizes %d %d not allowed", (int)keySize, (int)valueSize);
|
||||
return;
|
||||
}
|
||||
|
||||
const void* value = data.data();
|
||||
|
||||
BlobCache* bc = getBlobCacheLocked();
|
||||
bc->set(key.data(), keySize, value, valueSize);
|
||||
|
||||
if (!mSavePending && mDeferredSaveDelay > 0) {
|
||||
mSavePending = true;
|
||||
std::thread deferredSaveThread([this]() {
|
||||
sleep(mDeferredSaveDelay);
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
ATRACE_NAME("ShaderCache::saveToDisk");
|
||||
if (mInitialized && mBlobCache) {
|
||||
mBlobCache->writeToFile();
|
||||
}
|
||||
mSavePending = false;
|
||||
});
|
||||
deferredSaveThread.detach();
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace skiapipeline */
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
148
libs/hwui/pipeline/skia/ShaderCache.h
Normal file
148
libs/hwui/pipeline/skia/ShaderCache.h
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <GrContextOptions.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class BlobCache;
|
||||
class FileBlobCache;
|
||||
|
||||
namespace uirenderer {
|
||||
namespace skiapipeline {
|
||||
|
||||
class ShaderCache : public GrContextOptions::PersistentCache {
|
||||
public:
|
||||
/**
|
||||
* "get" returns a pointer to the singleton ShaderCache object. This
|
||||
* singleton object will never be destroyed.
|
||||
*/
|
||||
ANDROID_API static ShaderCache& get();
|
||||
|
||||
/**
|
||||
* "initShaderDiskCache" loads the serialized cache contents from disk and puts the ShaderCache
|
||||
* into an initialized state, such that it is able to insert and retrieve entries from the
|
||||
* cache. This should be called when HWUI pipeline is initialized. When not in the initialized
|
||||
* state the load and store methods will return without performing any cache operations.
|
||||
*/
|
||||
virtual void initShaderDiskCache();
|
||||
|
||||
/**
|
||||
* "setFilename" sets the name of the file that should be used to store
|
||||
* cache contents from one program invocation to another. This function does not perform any
|
||||
* disk operation and it should be invoked before "initShaderCache".
|
||||
*/
|
||||
virtual void setFilename(const char* filename);
|
||||
|
||||
/**
|
||||
* "load" attempts to retrieve the value blob associated with a given key
|
||||
* blob from cache. This will be called by Skia, when it needs to compile a new SKSL shader.
|
||||
*/
|
||||
sk_sp<SkData> load(const SkData& key) override;
|
||||
|
||||
/**
|
||||
* "store" attempts to insert a new key/value blob pair into the cache.
|
||||
* This will be called by Skia after it compiled a new SKSL shader
|
||||
*/
|
||||
void store(const SkData& key, const SkData& data) override;
|
||||
|
||||
private:
|
||||
// Creation and (the lack of) destruction is handled internally.
|
||||
ShaderCache();
|
||||
|
||||
// Copying is disallowed.
|
||||
ShaderCache(const ShaderCache&) = delete;
|
||||
void operator=(const ShaderCache&) = delete;
|
||||
|
||||
/**
|
||||
* "getBlobCacheLocked" returns the BlobCache object being used to store the
|
||||
* key/value blob pairs. If the BlobCache object has not yet been created,
|
||||
* this will do so, loading the serialized cache contents from disk if
|
||||
* possible.
|
||||
*/
|
||||
BlobCache* getBlobCacheLocked();
|
||||
|
||||
/**
|
||||
* "mInitialized" indicates whether the ShaderCache is in the initialized
|
||||
* state. It is initialized to false at construction time, and gets set to
|
||||
* true when initialize is called.
|
||||
* When in this state, the cache behaves as normal. When not,
|
||||
* the load and store methods will return without performing any cache
|
||||
* operations.
|
||||
*/
|
||||
bool mInitialized = false;
|
||||
|
||||
/**
|
||||
* "mBlobCache" is the cache in which the key/value blob pairs are stored. It
|
||||
* is initially NULL, and will be initialized by getBlobCacheLocked the
|
||||
* first time it's needed.
|
||||
* The blob cache contains the Android build number. We treat version mismatches as an empty
|
||||
* cache (logic implemented in BlobCache::unflatten).
|
||||
*/
|
||||
std::unique_ptr<FileBlobCache> mBlobCache;
|
||||
|
||||
/**
|
||||
* "mFilename" is the name of the file for storing cache contents in between
|
||||
* program invocations. It is initialized to an empty string at
|
||||
* construction time, and can be set with the setCacheFilename method. An
|
||||
* empty string indicates that the cache should not be saved to or restored
|
||||
* from disk.
|
||||
*/
|
||||
std::string mFilename;
|
||||
|
||||
/**
|
||||
* "mSavePending" indicates whether or not a deferred save operation is
|
||||
* pending. Each time a key/value pair is inserted into the cache via
|
||||
* load, a deferred save is initiated if one is not already pending.
|
||||
* This will wait some amount of time and then trigger a save of the cache
|
||||
* contents to disk.
|
||||
*/
|
||||
bool mSavePending = false;
|
||||
|
||||
/**
|
||||
* "mObservedBlobValueSize" is the maximum value size observed by the cache reading function.
|
||||
*/
|
||||
size_t mObservedBlobValueSize = 20*1024;
|
||||
|
||||
/**
|
||||
* The time in seconds to wait before saving newly inserted cache entries.
|
||||
*/
|
||||
unsigned int mDeferredSaveDelay = 4;
|
||||
|
||||
/**
|
||||
* "mMutex" is the mutex used to prevent concurrent access to the member
|
||||
* variables. It must be locked whenever the member variables are accessed.
|
||||
*/
|
||||
mutable std::mutex mMutex;
|
||||
|
||||
/**
|
||||
* "sCache" is the singleton ShaderCache object.
|
||||
*/
|
||||
static ShaderCache sCache;
|
||||
|
||||
friend class ShaderCacheTestUtils; //used for unit testing
|
||||
};
|
||||
|
||||
} /* namespace skiapipeline */
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "Layer.h"
|
||||
#include "RenderThread.h"
|
||||
#include "pipeline/skia/ShaderCache.h"
|
||||
#include "renderstate/RenderState.h"
|
||||
|
||||
#include <GrContextOptions.h>
|
||||
@@ -127,6 +128,8 @@ void CacheManager::configureContext(GrContextOptions* contextOptions) {
|
||||
}
|
||||
contextOptions->fExecutor = mTaskProcessor.get();
|
||||
}
|
||||
|
||||
contextOptions->fPersistentCache = &skiapipeline::ShaderCache::get();
|
||||
}
|
||||
|
||||
void CacheManager::trimMemory(TrimMemoryMode mode) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "RenderThread.h"
|
||||
|
||||
#include "pipeline/skia/ShaderCache.h"
|
||||
#include "CanvasContext.h"
|
||||
#include "EglManager.h"
|
||||
#include "OpenGLReadback.h"
|
||||
@@ -105,6 +106,7 @@ void RenderThread::initThreadLocals() {
|
||||
mRenderState = new RenderState(*this);
|
||||
mVkManager = new VulkanManager(*this);
|
||||
mCacheManager = new CacheManager(mDisplayInfo);
|
||||
uirenderer::skiapipeline::ShaderCache::get().initShaderDiskCache();
|
||||
}
|
||||
|
||||
void RenderThread::dumpGraphicsMemory(int fd) {
|
||||
|
||||
@@ -107,8 +107,11 @@ void VulkanManager::initialize() {
|
||||
|
||||
mGetDeviceQueue(mBackendContext->fDevice, mPresentQueueIndex, 0, &mPresentQueue);
|
||||
|
||||
GrContextOptions options;
|
||||
options.fDisableDistanceFieldPaths = true;
|
||||
mRenderThread.cacheManager().configureContext(&options);
|
||||
mRenderThread.setGrContext(
|
||||
GrContext::Create(kVulkan_GrBackend, (GrBackendContext)mBackendContext.get()));
|
||||
GrContext::Create(kVulkan_GrBackend, (GrBackendContext)mBackendContext.get(), options));
|
||||
DeviceInfo::initialize(mRenderThread.getGrContext()->caps()->maxRenderTargetSize());
|
||||
|
||||
if (Properties::enablePartialUpdates && Properties::useBufferAge) {
|
||||
|
||||
176
libs/hwui/tests/unit/ShaderCacheTests.cpp
Normal file
176
libs/hwui/tests/unit/ShaderCacheTests.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <dirent.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <cstdint>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <utils/Log.h>
|
||||
#include "pipeline/skia/ShaderCache.h"
|
||||
#include "FileBlobCache.h"
|
||||
|
||||
using namespace android::uirenderer::skiapipeline;
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
namespace skiapipeline {
|
||||
|
||||
class ShaderCacheTestUtils {
|
||||
public:
|
||||
/**
|
||||
* "setSaveDelay" sets the time in seconds to wait before saving newly inserted cache entries.
|
||||
* If set to 0, then deferred save is disabled.
|
||||
*/
|
||||
static void setSaveDelay(ShaderCache& cache, unsigned int saveDelay) {
|
||||
cache.mDeferredSaveDelay = saveDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* "terminate" optionally stores the BlobCache on disk and release all in-memory cache.
|
||||
* Next call to "initShaderDiskCache" will load again the in-memory cache from disk.
|
||||
*/
|
||||
static void terminate(ShaderCache& cache, bool saveContent) {
|
||||
std::lock_guard<std::mutex> lock(cache.mMutex);
|
||||
if (cache.mInitialized && cache.mBlobCache && saveContent) {
|
||||
cache.mBlobCache->writeToFile();
|
||||
}
|
||||
cache.mBlobCache = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace skiapipeline */
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
std::string getExternalStorageFolder() {
|
||||
return getenv("EXTERNAL_STORAGE");
|
||||
}
|
||||
|
||||
bool folderExist(const std::string& folderName) {
|
||||
DIR* dir = opendir(folderName.c_str());
|
||||
if (dir) {
|
||||
closedir(dir);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool checkShader(const sk_sp<SkData>& shader, const char* program) {
|
||||
sk_sp<SkData> shader2 = SkData::MakeWithCString(program);
|
||||
return shader->size() == shader2->size()
|
||||
&& 0 == memcmp(shader->data(), shader2->data(), shader->size());
|
||||
}
|
||||
|
||||
bool checkShader(const sk_sp<SkData>& shader, std::vector<char>& program) {
|
||||
sk_sp<SkData> shader2 = SkData::MakeWithCopy(program.data(), program.size());
|
||||
return shader->size() == shader2->size()
|
||||
&& 0 == memcmp(shader->data(), shader2->data(), shader->size());
|
||||
}
|
||||
|
||||
void setShader(sk_sp<SkData>& shader, const char* program) {
|
||||
shader = SkData::MakeWithCString(program);
|
||||
}
|
||||
|
||||
void setShader(sk_sp<SkData>& shader, std::vector<char>& program) {
|
||||
shader = SkData::MakeWithCopy(program.data(), program.size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define GrProgramDescTest(a) (*SkData::MakeWithCString(#a).get())
|
||||
|
||||
TEST(ShaderCacheTest, testWriteAndRead) {
|
||||
if (!folderExist(getExternalStorageFolder())) {
|
||||
//don't run the test if external storage folder is not available
|
||||
return;
|
||||
}
|
||||
std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
|
||||
std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
|
||||
|
||||
//remove any test files from previous test run
|
||||
int deleteFile = remove(cacheFile1.c_str());
|
||||
ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
|
||||
|
||||
//read the cache from a file that does not exist
|
||||
ShaderCache::get().setFilename(cacheFile1.c_str());
|
||||
ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); //disable deferred save
|
||||
ShaderCache::get().initShaderDiskCache();
|
||||
|
||||
//read a key - should not be found since the cache is empty
|
||||
sk_sp<SkData> outVS;
|
||||
ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
|
||||
|
||||
//write to the in-memory cache without storing on disk and verify we read the same values
|
||||
sk_sp<SkData> inVS;
|
||||
setShader(inVS, "sassas");
|
||||
ShaderCache::get().store(GrProgramDescTest(100), *inVS.get());
|
||||
setShader(inVS, "someVS");
|
||||
ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
|
||||
ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(100))), sk_sp<SkData>());
|
||||
ASSERT_TRUE(checkShader(outVS, "sassas"));
|
||||
ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
|
||||
ASSERT_TRUE(checkShader(outVS, "someVS"));
|
||||
|
||||
//store content to disk and release in-memory cache
|
||||
ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
|
||||
|
||||
//change to a file that does not exist and verify load fails
|
||||
ShaderCache::get().setFilename(cacheFile2.c_str());
|
||||
ShaderCache::get().initShaderDiskCache();
|
||||
ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
|
||||
ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
|
||||
|
||||
//load again content from disk from an existing file and check the data is read correctly
|
||||
ShaderCache::get().setFilename(cacheFile1.c_str());
|
||||
ShaderCache::get().initShaderDiskCache();
|
||||
sk_sp<SkData> outVS2;
|
||||
ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
|
||||
ASSERT_TRUE(checkShader(outVS2, "someVS"));
|
||||
|
||||
//change data, store to disk, read back again and verify data has been changed
|
||||
setShader(inVS, "ewData1");
|
||||
ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
|
||||
ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
|
||||
ShaderCache::get().initShaderDiskCache();
|
||||
ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
|
||||
ASSERT_TRUE(checkShader(outVS2, "ewData1"));
|
||||
|
||||
|
||||
//write and read big data chunk (50K)
|
||||
size_t dataSize = 50*1024;
|
||||
std::vector<char> dataBuffer(dataSize);
|
||||
for (size_t i = 0; i < dataSize; i++) {
|
||||
dataBuffer[0] = dataSize % 256;
|
||||
}
|
||||
setShader(inVS, dataBuffer);
|
||||
ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
|
||||
ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
|
||||
ShaderCache::get().initShaderDiskCache();
|
||||
ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
|
||||
ASSERT_TRUE(checkShader(outVS2, dataBuffer));
|
||||
|
||||
ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
|
||||
remove(cacheFile1.c_str());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user