1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "gpu/command_buffer/service/memory_program_cache.h" 6 7 #include "base/base64.h" 8 #include "base/command_line.h" 9 #include "base/metrics/histogram.h" 10 #include "base/sha1.h" 11 #include "base/strings/string_number_conversions.h" 12 #include "gpu/command_buffer/common/constants.h" 13 #include "gpu/command_buffer/service/disk_cache_proto.pb.h" 14 #include "gpu/command_buffer/service/gl_utils.h" 15 #include "gpu/command_buffer/service/gles2_cmd_decoder.h" 16 #include "gpu/command_buffer/service/gpu_switches.h" 17 #include "gpu/command_buffer/service/shader_manager.h" 18 #include "ui/gl/gl_bindings.h" 19 20 namespace { 21 22 size_t GetCacheSizeBytes() { 23 size_t size; 24 const CommandLine* command_line = CommandLine::ForCurrentProcess(); 25 if (command_line->HasSwitch(switches::kGpuProgramCacheSizeKb) && 26 base::StringToSizeT(command_line->GetSwitchValueNative( 27 switches::kGpuProgramCacheSizeKb), 28 &size)) { 29 return size * 1024; 30 } 31 return gpu::kDefaultMaxProgramCacheMemoryBytes; 32 } 33 34 } // anonymous namespace 35 36 namespace gpu { 37 namespace gles2 { 38 39 namespace { 40 41 enum ShaderMapType { 42 ATTRIB_MAP = 0, 43 UNIFORM_MAP 44 }; 45 46 void StoreShaderInfo(ShaderMapType type, ShaderProto *proto, 47 const ShaderTranslator::VariableMap& map) { 48 ShaderTranslator::VariableMap::const_iterator iter; 49 for (iter = map.begin(); iter != map.end(); iter++) { 50 ShaderInfoProto* info; 51 if (type == UNIFORM_MAP) { 52 info = proto->add_uniforms(); 53 } else { 54 info = proto->add_attribs(); 55 } 56 57 info->set_key(iter->first); 58 info->set_type(iter->second.type); 59 info->set_size(iter->second.size); 60 info->set_name(iter->second.name); 61 } 62 } 63 64 void RetrieveShaderInfo(const ShaderInfoProto& proto, 65 ShaderTranslator::VariableMap* map) { 66 ShaderTranslator::VariableInfo info(proto.type(), proto.size(), 67 proto.name()); 68 (*map)[proto.key()] = info; 69 } 70 71 void FillShaderProto(ShaderProto* proto, const char* sha, 72 const Shader* shader) { 73 proto->set_sha(sha, gpu::gles2::ProgramCache::kHashLength); 74 StoreShaderInfo(ATTRIB_MAP, proto, shader->attrib_map()); 75 StoreShaderInfo(UNIFORM_MAP, proto, shader->uniform_map()); 76 } 77 78 void RunShaderCallback(const ShaderCacheCallback& callback, 79 GpuProgramProto* proto, 80 std::string sha_string) { 81 std::string shader; 82 proto->SerializeToString(&shader); 83 84 std::string key; 85 base::Base64Encode(sha_string, &key); 86 callback.Run(key, shader); 87 } 88 89 } // namespace 90 91 MemoryProgramCache::MemoryProgramCache() 92 : max_size_bytes_(GetCacheSizeBytes()), 93 curr_size_bytes_(0), 94 store_(ProgramMRUCache::NO_AUTO_EVICT) { 95 } 96 97 MemoryProgramCache::MemoryProgramCache(const size_t max_cache_size_bytes) 98 : max_size_bytes_(max_cache_size_bytes), 99 curr_size_bytes_(0), 100 store_(ProgramMRUCache::NO_AUTO_EVICT) { 101 } 102 103 MemoryProgramCache::~MemoryProgramCache() {} 104 105 void MemoryProgramCache::ClearBackend() { 106 store_.Clear(); 107 DCHECK_EQ(0U, curr_size_bytes_); 108 } 109 110 ProgramCache::ProgramLoadResult MemoryProgramCache::LoadLinkedProgram( 111 GLuint program, 112 Shader* shader_a, 113 const ShaderTranslatorInterface* translator_a, 114 Shader* shader_b, 115 const ShaderTranslatorInterface* translator_b, 116 const LocationMap* bind_attrib_location_map, 117 const ShaderCacheCallback& shader_callback) { 118 char a_sha[kHashLength]; 119 char b_sha[kHashLength]; 120 DCHECK(shader_a && shader_a->signature_source() && 121 shader_b && shader_b->signature_source()); 122 ComputeShaderHash( 123 *shader_a->signature_source(), translator_a, a_sha); 124 ComputeShaderHash( 125 *shader_b->signature_source(), translator_b, b_sha); 126 127 char sha[kHashLength]; 128 ComputeProgramHash(a_sha, 129 b_sha, 130 bind_attrib_location_map, 131 sha); 132 const std::string sha_string(sha, kHashLength); 133 134 ProgramMRUCache::iterator found = store_.Get(sha_string); 135 if (found == store_.end()) { 136 return PROGRAM_LOAD_FAILURE; 137 } 138 const scoped_refptr<ProgramCacheValue> value = found->second; 139 glProgramBinary(program, 140 value->format(), 141 static_cast<const GLvoid*>(value->data()), 142 value->length()); 143 GLint success = 0; 144 glGetProgramiv(program, GL_LINK_STATUS, &success); 145 if (success == GL_FALSE) { 146 return PROGRAM_LOAD_FAILURE; 147 } 148 shader_a->set_attrib_map(value->attrib_map_0()); 149 shader_a->set_uniform_map(value->uniform_map_0()); 150 shader_b->set_attrib_map(value->attrib_map_1()); 151 shader_b->set_uniform_map(value->uniform_map_1()); 152 153 if (!shader_callback.is_null() && 154 !CommandLine::ForCurrentProcess()->HasSwitch( 155 switches::kDisableGpuShaderDiskCache)) { 156 scoped_ptr<GpuProgramProto> proto( 157 GpuProgramProto::default_instance().New()); 158 proto->set_sha(sha, kHashLength); 159 proto->set_format(value->format()); 160 proto->set_program(value->data(), value->length()); 161 162 FillShaderProto(proto->mutable_vertex_shader(), a_sha, shader_a); 163 FillShaderProto(proto->mutable_fragment_shader(), b_sha, shader_b); 164 RunShaderCallback(shader_callback, proto.get(), sha_string); 165 } 166 167 return PROGRAM_LOAD_SUCCESS; 168 } 169 170 void MemoryProgramCache::SaveLinkedProgram( 171 GLuint program, 172 const Shader* shader_a, 173 const ShaderTranslatorInterface* translator_a, 174 const Shader* shader_b, 175 const ShaderTranslatorInterface* translator_b, 176 const LocationMap* bind_attrib_location_map, 177 const ShaderCacheCallback& shader_callback) { 178 GLenum format; 179 GLsizei length = 0; 180 glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH_OES, &length); 181 if (length == 0 || static_cast<unsigned int>(length) > max_size_bytes_) { 182 return; 183 } 184 scoped_ptr<char[]> binary(new char[length]); 185 glGetProgramBinary(program, 186 length, 187 NULL, 188 &format, 189 binary.get()); 190 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.ProgramBinarySizeBytes", length); 191 192 char a_sha[kHashLength]; 193 char b_sha[kHashLength]; 194 DCHECK(shader_a && shader_a->signature_source() && 195 shader_b && shader_b->signature_source()); 196 ComputeShaderHash( 197 *shader_a->signature_source(), translator_a, a_sha); 198 ComputeShaderHash( 199 *shader_b->signature_source(), translator_b, b_sha); 200 201 char sha[kHashLength]; 202 ComputeProgramHash(a_sha, 203 b_sha, 204 bind_attrib_location_map, 205 sha); 206 const std::string sha_string(sha, sizeof(sha)); 207 208 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeBeforeKb", 209 curr_size_bytes_ / 1024); 210 211 // Evict any cached program with the same key in favor of the least recently 212 // accessed. 213 ProgramMRUCache::iterator existing = store_.Peek(sha_string); 214 if(existing != store_.end()) 215 store_.Erase(existing); 216 217 while (curr_size_bytes_ + length > max_size_bytes_) { 218 DCHECK(!store_.empty()); 219 store_.Erase(store_.rbegin()); 220 } 221 222 if (!shader_callback.is_null() && 223 !CommandLine::ForCurrentProcess()->HasSwitch( 224 switches::kDisableGpuShaderDiskCache)) { 225 scoped_ptr<GpuProgramProto> proto( 226 GpuProgramProto::default_instance().New()); 227 proto->set_sha(sha, kHashLength); 228 proto->set_format(format); 229 proto->set_program(binary.get(), length); 230 231 FillShaderProto(proto->mutable_vertex_shader(), a_sha, shader_a); 232 FillShaderProto(proto->mutable_fragment_shader(), b_sha, shader_b); 233 RunShaderCallback(shader_callback, proto.get(), sha_string); 234 } 235 236 store_.Put(sha_string, 237 new ProgramCacheValue(length, 238 format, 239 binary.release(), 240 sha_string, 241 a_sha, 242 shader_a->attrib_map(), 243 shader_a->uniform_map(), 244 b_sha, 245 shader_b->attrib_map(), 246 shader_b->uniform_map(), 247 this)); 248 249 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeAfterKb", 250 curr_size_bytes_ / 1024); 251 } 252 253 void MemoryProgramCache::LoadProgram(const std::string& program) { 254 scoped_ptr<GpuProgramProto> proto(GpuProgramProto::default_instance().New()); 255 if (proto->ParseFromString(program)) { 256 ShaderTranslator::VariableMap vertex_attribs; 257 ShaderTranslator::VariableMap vertex_uniforms; 258 259 for (int i = 0; i < proto->vertex_shader().attribs_size(); i++) { 260 RetrieveShaderInfo(proto->vertex_shader().attribs(i), &vertex_attribs); 261 } 262 263 for (int i = 0; i < proto->vertex_shader().uniforms_size(); i++) { 264 RetrieveShaderInfo(proto->vertex_shader().uniforms(i), &vertex_uniforms); 265 } 266 267 ShaderTranslator::VariableMap fragment_attribs; 268 ShaderTranslator::VariableMap fragment_uniforms; 269 270 for (int i = 0; i < proto->fragment_shader().attribs_size(); i++) { 271 RetrieveShaderInfo(proto->fragment_shader().attribs(i), 272 &fragment_attribs); 273 } 274 275 for (int i = 0; i < proto->fragment_shader().uniforms_size(); i++) { 276 RetrieveShaderInfo(proto->fragment_shader().uniforms(i), 277 &fragment_uniforms); 278 } 279 280 scoped_ptr<char[]> binary(new char[proto->program().length()]); 281 memcpy(binary.get(), proto->program().c_str(), proto->program().length()); 282 283 store_.Put(proto->sha(), 284 new ProgramCacheValue(proto->program().length(), 285 proto->format(), 286 binary.release(), 287 proto->sha(), 288 proto->vertex_shader().sha().c_str(), 289 vertex_attribs, 290 vertex_uniforms, 291 proto->fragment_shader().sha().c_str(), 292 fragment_attribs, 293 fragment_uniforms, 294 this)); 295 296 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeAfterKb", 297 curr_size_bytes_ / 1024); 298 } else { 299 LOG(ERROR) << "Failed to parse proto file."; 300 } 301 } 302 303 MemoryProgramCache::ProgramCacheValue::ProgramCacheValue( 304 GLsizei length, 305 GLenum format, 306 const char* data, 307 const std::string& program_hash, 308 const char* shader_0_hash, 309 const ShaderTranslator::VariableMap& attrib_map_0, 310 const ShaderTranslator::VariableMap& uniform_map_0, 311 const char* shader_1_hash, 312 const ShaderTranslator::VariableMap& attrib_map_1, 313 const ShaderTranslator::VariableMap& uniform_map_1, 314 MemoryProgramCache* program_cache) 315 : length_(length), 316 format_(format), 317 data_(data), 318 program_hash_(program_hash), 319 shader_0_hash_(shader_0_hash, kHashLength), 320 attrib_map_0_(attrib_map_0), 321 uniform_map_0_(uniform_map_0), 322 shader_1_hash_(shader_1_hash, kHashLength), 323 attrib_map_1_(attrib_map_1), 324 uniform_map_1_(uniform_map_1), 325 program_cache_(program_cache) { 326 program_cache_->curr_size_bytes_ += length_; 327 program_cache_->LinkedProgramCacheSuccess(program_hash); 328 } 329 330 MemoryProgramCache::ProgramCacheValue::~ProgramCacheValue() { 331 program_cache_->curr_size_bytes_ -= length_; 332 program_cache_->Evict(program_hash_); 333 } 334 335 } // namespace gles2 336 } // namespace gpu 337