- add sources.
[platform/framework/web/crosswalk.git] / src / gpu / command_buffer / service / memory_program_cache.cc
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