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