1/* 2 * Copyright 2018 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 "include/utils/SkBase64.h" 9#include "src/core/SkMD5.h" 10#include "src/core/SkReadBuffer.h" 11#include "src/gpu/GrPersistentCacheUtils.h" 12#include "tools/gpu/MemoryCache.h" 13 14#if defined(SK_VULKAN) 15#include "src/gpu/vk/GrVkGpu.h" 16#endif 17 18// Change this to 1 to log cache hits/misses/stores using SkDebugf. 19#define LOG_MEMORY_CACHE 0 20 21static SkString data_to_str(const SkData& data) { 22 size_t encodeLength = SkBase64::Encode(data.data(), data.size(), nullptr); 23 SkString str; 24 str.resize(encodeLength); 25 SkBase64::Encode(data.data(), data.size(), str.writable_str()); 26 static constexpr size_t kMaxLength = 60; 27 static constexpr char kTail[] = "..."; 28 static const size_t kTailLen = strlen(kTail); 29 bool overlength = encodeLength > kMaxLength; 30 if (overlength) { 31 str = SkString(str.c_str(), kMaxLength - kTailLen); 32 str.append(kTail); 33 } 34 return str; 35} 36 37namespace sk_gpu_test { 38 39sk_sp<SkData> MemoryCache::load(const SkData& key) { 40 auto result = fMap.find(key); 41 if (result == fMap.end()) { 42 if (LOG_MEMORY_CACHE) { 43 SkDebugf("Load Key: %s\n\tNot Found.\n\n", data_to_str(key).c_str()); 44 } 45 ++fCacheMissCnt; 46 return nullptr; 47 } 48 if (LOG_MEMORY_CACHE) { 49 SkDebugf("Load Key: %s\n\tFound Data: %s\n\n", data_to_str(key).c_str(), 50 data_to_str(*result->second.fData).c_str()); 51 } 52 result->second.fHitCount++; 53 return result->second.fData; 54} 55 56void MemoryCache::store(const SkData& key, const SkData& data, const SkString& description) { 57 if (LOG_MEMORY_CACHE) { 58 SkDebugf("Store Key: %s\n\tData: %s\n\n", data_to_str(key).c_str(), 59 data_to_str(data).c_str()); 60 } 61 ++fCacheStoreCnt; 62 fMap[Key(key)] = Value(data, description); 63} 64 65void MemoryCache::writeShadersToDisk(const char* path, GrBackendApi api) { 66 if (GrBackendApi::kOpenGL != api && GrBackendApi::kVulkan != api) { 67 return; 68 } 69 70 for (auto it = fMap.begin(); it != fMap.end(); ++it) { 71 SkMD5 hash; 72 size_t bytesToHash = it->first.fKey->size(); 73#if defined(SK_VULKAN) 74 if (GrBackendApi::kVulkan == api) { 75 // Vulkan stores two kinds of data in the cache (shaders and pipelines). The last four 76 // bytes of the key identify which one we have. We only want to extract shaders. 77 // Additionally, we don't want to hash the tag bytes, so we get the same keys as GL, 78 // which is good for cross-checking code generation and performance. 79 GrVkGpu::PersistentCacheKeyType vkKeyType; 80 SkASSERT(bytesToHash >= sizeof(vkKeyType)); 81 bytesToHash -= sizeof(vkKeyType); 82 memcpy(&vkKeyType, it->first.fKey->bytes() + bytesToHash, sizeof(vkKeyType)); 83 if (vkKeyType != GrVkGpu::kShader_PersistentCacheKeyType) { 84 continue; 85 } 86 } 87#endif 88 hash.write(it->first.fKey->bytes(), bytesToHash); 89 SkMD5::Digest digest = hash.finish(); 90 SkString md5; 91 for (int i = 0; i < 16; ++i) { 92 md5.appendf("%02x", digest.data[i]); 93 } 94 95 SkSL::Program::Inputs inputsIgnored[kGrShaderTypeCount]; 96 SkSL::String shaders[kGrShaderTypeCount]; 97 const SkData* data = it->second.fData.get(); 98 const SkString& description = it->second.fDescription; 99 SkReadBuffer reader(data->data(), data->size()); 100 GrPersistentCacheUtils::GetType(&reader); // Shader type tag 101 GrPersistentCacheUtils::UnpackCachedShaders(&reader, shaders, 102 inputsIgnored, kGrShaderTypeCount); 103 104 // Even with the SPIR-V switches, it seems like we must use .spv, or malisc tries to 105 // run glslang on the input. 106 { 107 const char* ext = GrBackendApi::kOpenGL == api ? "frag" : "frag.spv"; 108 SkString filename = SkStringPrintf("%s/%s.%s", path, md5.c_str(), ext); 109 SkFILEWStream file(filename.c_str()); 110 file.write(shaders[kFragment_GrShaderType].c_str(), 111 shaders[kFragment_GrShaderType].size()); 112 } 113 { 114 const char* ext = GrBackendApi::kOpenGL == api ? "vert" : "vert.spv"; 115 SkString filename = SkStringPrintf("%s/%s.%s", path, md5.c_str(), ext); 116 SkFILEWStream file(filename.c_str()); 117 file.write(shaders[kVertex_GrShaderType].c_str(), 118 shaders[kVertex_GrShaderType].size()); 119 } 120 121 if (!description.isEmpty()) { 122 const char* ext = "key"; 123 SkString filename = SkStringPrintf("%s/%s.%s", path, md5.c_str(), ext); 124 SkFILEWStream file(filename.c_str()); 125 file.write(description.c_str(), description.size()); 126 } 127 } 128} 129 130} // namespace sk_gpu_test 131