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 "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