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
21namespace {
22
23size_t GetCacheSizeBytes() {
24  const CommandLine* command_line = CommandLine::ForCurrentProcess();
25  if (command_line->HasSwitch(switches::kGpuProgramCacheSizeKb)) {
26    size_t size;
27    if (base::StringToSizeT(
28        command_line->GetSwitchValueNative(switches::kGpuProgramCacheSizeKb),
29        &size))
30      return size * 1024;
31  }
32  return gpu::kDefaultMaxProgramCacheMemoryBytes;
33}
34
35}  // anonymous namespace
36
37namespace gpu {
38namespace gles2 {
39
40namespace {
41
42enum ShaderMapType {
43  ATTRIB_MAP = 0,
44  UNIFORM_MAP,
45  VARYING_MAP
46};
47
48void 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
75void 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
83void 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
91void 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
104MemoryProgramCache::MemoryProgramCache()
105    : max_size_bytes_(GetCacheSizeBytes()),
106      curr_size_bytes_(0),
107      store_(ProgramMRUCache::NO_AUTO_EVICT) {
108}
109
110MemoryProgramCache::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
116MemoryProgramCache::~MemoryProgramCache() {}
117
118void MemoryProgramCache::ClearBackend() {
119  store_.Clear();
120  DCHECK_EQ(0U, curr_size_bytes_);
121}
122
123ProgramCache::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().empty() &&
134         shader_b && !shader_b->signature_source().empty());
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
185void 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().empty() &&
210         shader_b && !shader_b->signature_source().empty());
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
270void 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
333MemoryProgramCache::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
364MemoryProgramCache::ProgramCacheValue::~ProgramCacheValue() {
365  program_cache_->curr_size_bytes_ -= length_;
366  program_cache_->Evict(program_hash_);
367}
368
369}  // namespace gles2
370}  // namespace gpu
371