Elim cr-checkbox
[chromium-blink-merge.git] / gpu / command_buffer / service / memory_program_cache.cc
blob74809a98d7399f79df3c81402583ad43365e9df5
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.
5 #include "gpu/command_buffer/service/memory_program_cache.h"
7 #include "base/base64.h"
8 #include "base/callback.h"
9 #include "base/command_line.h"
10 #include "base/metrics/histogram.h"
11 #include "base/sha1.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "gpu/command_buffer/common/constants.h"
14 #include "gpu/command_buffer/service/disk_cache_proto.pb.h"
15 #include "gpu/command_buffer/service/gl_utils.h"
16 #include "gpu/command_buffer/service/gles2_cmd_decoder.h"
17 #include "gpu/command_buffer/service/gpu_switches.h"
18 #include "gpu/command_buffer/service/shader_manager.h"
19 #include "ui/gl/gl_bindings.h"
21 namespace {
23 size_t GetCacheSizeBytes() {
24 const base::CommandLine* command_line =
25 base::CommandLine::ForCurrentProcess();
26 if (command_line->HasSwitch(switches::kGpuProgramCacheSizeKb)) {
27 size_t size;
28 if (base::StringToSizeT(
29 command_line->GetSwitchValueNative(switches::kGpuProgramCacheSizeKb),
30 &size))
31 return size * 1024;
33 return gpu::kDefaultMaxProgramCacheMemoryBytes;
36 } // anonymous namespace
38 namespace gpu {
39 namespace gles2 {
41 namespace {
43 enum ShaderMapType {
44 ATTRIB_MAP = 0,
45 UNIFORM_MAP,
46 VARYING_MAP
49 void FillShaderVariableProto(
50 ShaderVariableProto* proto, const sh::ShaderVariable& variable) {
51 proto->set_type(variable.type);
52 proto->set_precision(variable.precision);
53 proto->set_name(variable.name);
54 proto->set_mapped_name(variable.mappedName);
55 proto->set_array_size(variable.arraySize);
56 proto->set_static_use(variable.staticUse);
57 for (size_t ii = 0; ii < variable.fields.size(); ++ii) {
58 ShaderVariableProto* field = proto->add_fields();
59 FillShaderVariableProto(field, variable.fields[ii]);
61 proto->set_struct_name(variable.structName);
64 void FillShaderAttributeProto(
65 ShaderAttributeProto* proto, const sh::Attribute& attrib) {
66 FillShaderVariableProto(proto->mutable_basic(), attrib);
67 proto->set_location(attrib.location);
70 void FillShaderUniformProto(
71 ShaderUniformProto* proto, const sh::Uniform& uniform) {
72 FillShaderVariableProto(proto->mutable_basic(), uniform);
75 void FillShaderVaryingProto(
76 ShaderVaryingProto* proto, const sh::Varying& varying) {
77 FillShaderVariableProto(proto->mutable_basic(), varying);
78 proto->set_interpolation(varying.interpolation);
79 proto->set_is_invariant(varying.isInvariant);
82 void FillShaderProto(ShaderProto* proto, const char* sha,
83 const Shader* shader) {
84 proto->set_sha(sha, gpu::gles2::ProgramCache::kHashLength);
85 for (AttributeMap::const_iterator iter = shader->attrib_map().begin();
86 iter != shader->attrib_map().end(); ++iter) {
87 ShaderAttributeProto* info = proto->add_attribs();
88 FillShaderAttributeProto(info, iter->second);
90 for (UniformMap::const_iterator iter = shader->uniform_map().begin();
91 iter != shader->uniform_map().end(); ++iter) {
92 ShaderUniformProto* info = proto->add_uniforms();
93 FillShaderUniformProto(info, iter->second);
95 for (VaryingMap::const_iterator iter = shader->varying_map().begin();
96 iter != shader->varying_map().end(); ++iter) {
97 ShaderVaryingProto* info = proto->add_varyings();
98 FillShaderVaryingProto(info, iter->second);
102 void RetrieveShaderVariableInfo(
103 const ShaderVariableProto& proto, sh::ShaderVariable* variable) {
104 variable->type = proto.type();
105 variable->precision = proto.precision();
106 variable->name = proto.name();
107 variable->mappedName = proto.mapped_name();
108 variable->arraySize = proto.array_size();
109 variable->staticUse = proto.static_use();
110 variable->fields.resize(proto.fields_size());
111 for (int ii = 0; ii < proto.fields_size(); ++ii)
112 RetrieveShaderVariableInfo(proto.fields(ii), &(variable->fields[ii]));
113 variable->structName = proto.struct_name();
116 void RetrieveShaderAttributeInfo(
117 const ShaderAttributeProto& proto, AttributeMap* map) {
118 sh::Attribute attrib;
119 RetrieveShaderVariableInfo(proto.basic(), &attrib);
120 attrib.location = proto.location();
121 (*map)[proto.basic().mapped_name()] = attrib;
124 void RetrieveShaderUniformInfo(
125 const ShaderUniformProto& proto, UniformMap* map) {
126 sh::Uniform uniform;
127 RetrieveShaderVariableInfo(proto.basic(), &uniform);
128 (*map)[proto.basic().mapped_name()] = uniform;
131 void RetrieveShaderVaryingInfo(
132 const ShaderVaryingProto& proto, VaryingMap* map) {
133 sh::Varying varying;
134 RetrieveShaderVariableInfo(proto.basic(), &varying);
135 varying.interpolation = static_cast<sh::InterpolationType>(
136 proto.interpolation());
137 varying.isInvariant = proto.is_invariant();
138 (*map)[proto.basic().mapped_name()] = varying;
141 void RunShaderCallback(const ShaderCacheCallback& callback,
142 GpuProgramProto* proto,
143 std::string sha_string) {
144 std::string shader;
145 proto->SerializeToString(&shader);
147 std::string key;
148 base::Base64Encode(sha_string, &key);
149 callback.Run(key, shader);
152 } // namespace
154 MemoryProgramCache::MemoryProgramCache()
155 : max_size_bytes_(GetCacheSizeBytes()),
156 curr_size_bytes_(0),
157 store_(ProgramMRUCache::NO_AUTO_EVICT) {
160 MemoryProgramCache::MemoryProgramCache(const size_t max_cache_size_bytes)
161 : max_size_bytes_(max_cache_size_bytes),
162 curr_size_bytes_(0),
163 store_(ProgramMRUCache::NO_AUTO_EVICT) {
166 MemoryProgramCache::~MemoryProgramCache() {}
168 void MemoryProgramCache::ClearBackend() {
169 store_.Clear();
170 DCHECK_EQ(0U, curr_size_bytes_);
173 ProgramCache::ProgramLoadResult MemoryProgramCache::LoadLinkedProgram(
174 GLuint program,
175 Shader* shader_a,
176 Shader* shader_b,
177 const LocationMap* bind_attrib_location_map,
178 const std::vector<std::string>& transform_feedback_varyings,
179 GLenum transform_feedback_buffer_mode,
180 const ShaderCacheCallback& shader_callback) {
181 char a_sha[kHashLength];
182 char b_sha[kHashLength];
183 DCHECK(shader_a && !shader_a->last_compiled_source().empty() &&
184 shader_b && !shader_b->last_compiled_source().empty());
185 ComputeShaderHash(
186 shader_a->last_compiled_signature(), a_sha);
187 ComputeShaderHash(
188 shader_b->last_compiled_signature(), b_sha);
190 char sha[kHashLength];
191 ComputeProgramHash(a_sha,
192 b_sha,
193 bind_attrib_location_map,
194 transform_feedback_varyings,
195 transform_feedback_buffer_mode,
196 sha);
197 const std::string sha_string(sha, kHashLength);
199 ProgramMRUCache::iterator found = store_.Get(sha_string);
200 if (found == store_.end()) {
201 return PROGRAM_LOAD_FAILURE;
203 const scoped_refptr<ProgramCacheValue> value = found->second;
204 glProgramBinary(program,
205 value->format(),
206 static_cast<const GLvoid*>(value->data()),
207 value->length());
208 GLint success = 0;
209 glGetProgramiv(program, GL_LINK_STATUS, &success);
210 if (success == GL_FALSE) {
211 return PROGRAM_LOAD_FAILURE;
213 shader_a->set_attrib_map(value->attrib_map_0());
214 shader_a->set_uniform_map(value->uniform_map_0());
215 shader_a->set_varying_map(value->varying_map_0());
216 shader_b->set_attrib_map(value->attrib_map_1());
217 shader_b->set_uniform_map(value->uniform_map_1());
218 shader_b->set_varying_map(value->varying_map_1());
220 if (!shader_callback.is_null() &&
221 !base::CommandLine::ForCurrentProcess()->HasSwitch(
222 switches::kDisableGpuShaderDiskCache)) {
223 scoped_ptr<GpuProgramProto> proto(
224 GpuProgramProto::default_instance().New());
225 proto->set_sha(sha, kHashLength);
226 proto->set_format(value->format());
227 proto->set_program(value->data(), value->length());
229 FillShaderProto(proto->mutable_vertex_shader(), a_sha, shader_a);
230 FillShaderProto(proto->mutable_fragment_shader(), b_sha, shader_b);
231 RunShaderCallback(shader_callback, proto.get(), sha_string);
234 return PROGRAM_LOAD_SUCCESS;
237 void MemoryProgramCache::SaveLinkedProgram(
238 GLuint program,
239 const Shader* shader_a,
240 const Shader* shader_b,
241 const LocationMap* bind_attrib_location_map,
242 const std::vector<std::string>& transform_feedback_varyings,
243 GLenum transform_feedback_buffer_mode,
244 const ShaderCacheCallback& shader_callback) {
245 GLenum format;
246 GLsizei length = 0;
247 glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH_OES, &length);
248 if (length == 0 || static_cast<unsigned int>(length) > max_size_bytes_) {
249 return;
251 scoped_ptr<char[]> binary(new char[length]);
252 glGetProgramBinary(program,
253 length,
254 NULL,
255 &format,
256 binary.get());
257 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.ProgramBinarySizeBytes", length);
259 char a_sha[kHashLength];
260 char b_sha[kHashLength];
261 DCHECK(shader_a && !shader_a->last_compiled_source().empty() &&
262 shader_b && !shader_b->last_compiled_source().empty());
263 ComputeShaderHash(
264 shader_a->last_compiled_signature(), a_sha);
265 ComputeShaderHash(
266 shader_b->last_compiled_signature(), b_sha);
268 char sha[kHashLength];
269 ComputeProgramHash(a_sha,
270 b_sha,
271 bind_attrib_location_map,
272 transform_feedback_varyings,
273 transform_feedback_buffer_mode,
274 sha);
275 const std::string sha_string(sha, sizeof(sha));
277 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeBeforeKb",
278 curr_size_bytes_ / 1024);
280 // Evict any cached program with the same key in favor of the least recently
281 // accessed.
282 ProgramMRUCache::iterator existing = store_.Peek(sha_string);
283 if(existing != store_.end())
284 store_.Erase(existing);
286 while (curr_size_bytes_ + length > max_size_bytes_) {
287 DCHECK(!store_.empty());
288 store_.Erase(store_.rbegin());
291 if (!shader_callback.is_null() &&
292 !base::CommandLine::ForCurrentProcess()->HasSwitch(
293 switches::kDisableGpuShaderDiskCache)) {
294 scoped_ptr<GpuProgramProto> proto(
295 GpuProgramProto::default_instance().New());
296 proto->set_sha(sha, kHashLength);
297 proto->set_format(format);
298 proto->set_program(binary.get(), length);
300 FillShaderProto(proto->mutable_vertex_shader(), a_sha, shader_a);
301 FillShaderProto(proto->mutable_fragment_shader(), b_sha, shader_b);
302 RunShaderCallback(shader_callback, proto.get(), sha_string);
305 store_.Put(sha_string,
306 new ProgramCacheValue(length,
307 format,
308 binary.release(),
309 sha_string,
310 a_sha,
311 shader_a->attrib_map(),
312 shader_a->uniform_map(),
313 shader_a->varying_map(),
314 b_sha,
315 shader_b->attrib_map(),
316 shader_b->uniform_map(),
317 shader_b->varying_map(),
318 this));
320 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeAfterKb",
321 curr_size_bytes_ / 1024);
324 void MemoryProgramCache::LoadProgram(const std::string& program) {
325 scoped_ptr<GpuProgramProto> proto(GpuProgramProto::default_instance().New());
326 if (proto->ParseFromString(program)) {
327 AttributeMap vertex_attribs;
328 UniformMap vertex_uniforms;
329 VaryingMap vertex_varyings;
330 for (int i = 0; i < proto->vertex_shader().attribs_size(); i++) {
331 RetrieveShaderAttributeInfo(proto->vertex_shader().attribs(i),
332 &vertex_attribs);
334 for (int i = 0; i < proto->vertex_shader().uniforms_size(); i++) {
335 RetrieveShaderUniformInfo(proto->vertex_shader().uniforms(i),
336 &vertex_uniforms);
338 for (int i = 0; i < proto->vertex_shader().varyings_size(); i++) {
339 RetrieveShaderVaryingInfo(proto->vertex_shader().varyings(i),
340 &vertex_varyings);
343 AttributeMap fragment_attribs;
344 UniformMap fragment_uniforms;
345 VaryingMap fragment_varyings;
346 for (int i = 0; i < proto->fragment_shader().attribs_size(); i++) {
347 RetrieveShaderAttributeInfo(proto->fragment_shader().attribs(i),
348 &fragment_attribs);
350 for (int i = 0; i < proto->fragment_shader().uniforms_size(); i++) {
351 RetrieveShaderUniformInfo(proto->fragment_shader().uniforms(i),
352 &fragment_uniforms);
354 for (int i = 0; i < proto->fragment_shader().varyings_size(); i++) {
355 RetrieveShaderVaryingInfo(proto->fragment_shader().varyings(i),
356 &fragment_varyings);
359 scoped_ptr<char[]> binary(new char[proto->program().length()]);
360 memcpy(binary.get(), proto->program().c_str(), proto->program().length());
362 store_.Put(proto->sha(),
363 new ProgramCacheValue(proto->program().length(),
364 proto->format(),
365 binary.release(),
366 proto->sha(),
367 proto->vertex_shader().sha().c_str(),
368 vertex_attribs,
369 vertex_uniforms,
370 vertex_varyings,
371 proto->fragment_shader().sha().c_str(),
372 fragment_attribs,
373 fragment_uniforms,
374 fragment_varyings,
375 this));
377 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeAfterKb",
378 curr_size_bytes_ / 1024);
379 } else {
380 LOG(ERROR) << "Failed to parse proto file.";
384 MemoryProgramCache::ProgramCacheValue::ProgramCacheValue(
385 GLsizei length,
386 GLenum format,
387 const char* data,
388 const std::string& program_hash,
389 const char* shader_0_hash,
390 const AttributeMap& attrib_map_0,
391 const UniformMap& uniform_map_0,
392 const VaryingMap& varying_map_0,
393 const char* shader_1_hash,
394 const AttributeMap& attrib_map_1,
395 const UniformMap& uniform_map_1,
396 const VaryingMap& varying_map_1,
397 MemoryProgramCache* program_cache)
398 : length_(length),
399 format_(format),
400 data_(data),
401 program_hash_(program_hash),
402 shader_0_hash_(shader_0_hash, kHashLength),
403 attrib_map_0_(attrib_map_0),
404 uniform_map_0_(uniform_map_0),
405 varying_map_0_(varying_map_0),
406 shader_1_hash_(shader_1_hash, kHashLength),
407 attrib_map_1_(attrib_map_1),
408 uniform_map_1_(uniform_map_1),
409 varying_map_1_(varying_map_1),
410 program_cache_(program_cache) {
411 program_cache_->curr_size_bytes_ += length_;
412 program_cache_->LinkedProgramCacheSuccess(program_hash);
415 MemoryProgramCache::ProgramCacheValue::~ProgramCacheValue() {
416 program_cache_->curr_size_bytes_ -= length_;
417 program_cache_->Evict(program_hash_);
420 } // namespace gles2
421 } // namespace gpu