Home | History | Annotate | Download | only in service
      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