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/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"
22 size_t GetCacheSizeBytes() {
23 const base::CommandLine
* command_line
=
24 base::CommandLine::ForCurrentProcess();
25 if (command_line
->HasSwitch(switches::kGpuProgramCacheSizeKb
)) {
27 if (base::StringToSizeT(
28 command_line
->GetSwitchValueNative(switches::kGpuProgramCacheSizeKb
),
32 return gpu::kDefaultMaxProgramCacheMemoryBytes
;
35 } // anonymous namespace
48 void FillShaderVariableProto(
49 ShaderVariableProto
* proto
, const sh::ShaderVariable
& variable
) {
50 proto
->set_type(variable
.type
);
51 proto
->set_precision(variable
.precision
);
52 proto
->set_name(variable
.name
);
53 proto
->set_mapped_name(variable
.mappedName
);
54 proto
->set_array_size(variable
.arraySize
);
55 proto
->set_static_use(variable
.staticUse
);
56 for (size_t ii
= 0; ii
< variable
.fields
.size(); ++ii
) {
57 ShaderVariableProto
* field
= proto
->add_fields();
58 FillShaderVariableProto(field
, variable
.fields
[ii
]);
60 proto
->set_struct_name(variable
.structName
);
63 void FillShaderAttributeProto(
64 ShaderAttributeProto
* proto
, const sh::Attribute
& attrib
) {
65 FillShaderVariableProto(proto
->mutable_basic(), attrib
);
66 proto
->set_location(attrib
.location
);
69 void FillShaderUniformProto(
70 ShaderUniformProto
* proto
, const sh::Uniform
& uniform
) {
71 FillShaderVariableProto(proto
->mutable_basic(), uniform
);
74 void FillShaderVaryingProto(
75 ShaderVaryingProto
* proto
, const sh::Varying
& varying
) {
76 FillShaderVariableProto(proto
->mutable_basic(), varying
);
77 proto
->set_interpolation(varying
.interpolation
);
78 proto
->set_is_invariant(varying
.isInvariant
);
81 void FillShaderProto(ShaderProto
* proto
, const char* sha
,
82 const Shader
* shader
) {
83 proto
->set_sha(sha
, gpu::gles2::ProgramCache::kHashLength
);
84 for (AttributeMap::const_iterator iter
= shader
->attrib_map().begin();
85 iter
!= shader
->attrib_map().end(); ++iter
) {
86 ShaderAttributeProto
* info
= proto
->add_attribs();
87 FillShaderAttributeProto(info
, iter
->second
);
89 for (UniformMap::const_iterator iter
= shader
->uniform_map().begin();
90 iter
!= shader
->uniform_map().end(); ++iter
) {
91 ShaderUniformProto
* info
= proto
->add_uniforms();
92 FillShaderUniformProto(info
, iter
->second
);
94 for (VaryingMap::const_iterator iter
= shader
->varying_map().begin();
95 iter
!= shader
->varying_map().end(); ++iter
) {
96 ShaderVaryingProto
* info
= proto
->add_varyings();
97 FillShaderVaryingProto(info
, iter
->second
);
101 void RetrieveShaderVariableInfo(
102 const ShaderVariableProto
& proto
, sh::ShaderVariable
* variable
) {
103 variable
->type
= proto
.type();
104 variable
->precision
= proto
.precision();
105 variable
->name
= proto
.name();
106 variable
->mappedName
= proto
.mapped_name();
107 variable
->arraySize
= proto
.array_size();
108 variable
->staticUse
= proto
.static_use();
109 variable
->fields
.resize(proto
.fields_size());
110 for (int ii
= 0; ii
< proto
.fields_size(); ++ii
)
111 RetrieveShaderVariableInfo(proto
.fields(ii
), &(variable
->fields
[ii
]));
112 variable
->structName
= proto
.struct_name();
115 void RetrieveShaderAttributeInfo(
116 const ShaderAttributeProto
& proto
, AttributeMap
* map
) {
117 sh::Attribute attrib
;
118 RetrieveShaderVariableInfo(proto
.basic(), &attrib
);
119 attrib
.location
= proto
.location();
120 (*map
)[proto
.basic().mapped_name()] = attrib
;
123 void RetrieveShaderUniformInfo(
124 const ShaderUniformProto
& proto
, UniformMap
* map
) {
126 RetrieveShaderVariableInfo(proto
.basic(), &uniform
);
127 (*map
)[proto
.basic().mapped_name()] = uniform
;
130 void RetrieveShaderVaryingInfo(
131 const ShaderVaryingProto
& proto
, VaryingMap
* map
) {
133 RetrieveShaderVariableInfo(proto
.basic(), &varying
);
134 varying
.interpolation
= static_cast<sh::InterpolationType
>(
135 proto
.interpolation());
136 varying
.isInvariant
= proto
.is_invariant();
137 (*map
)[proto
.basic().mapped_name()] = varying
;
140 void RunShaderCallback(const ShaderCacheCallback
& callback
,
141 GpuProgramProto
* proto
,
142 std::string sha_string
) {
144 proto
->SerializeToString(&shader
);
147 base::Base64Encode(sha_string
, &key
);
148 callback
.Run(key
, shader
);
153 MemoryProgramCache::MemoryProgramCache()
154 : max_size_bytes_(GetCacheSizeBytes()),
156 store_(ProgramMRUCache::NO_AUTO_EVICT
) {
159 MemoryProgramCache::MemoryProgramCache(const size_t max_cache_size_bytes
)
160 : max_size_bytes_(max_cache_size_bytes
),
162 store_(ProgramMRUCache::NO_AUTO_EVICT
) {
165 MemoryProgramCache::~MemoryProgramCache() {}
167 void MemoryProgramCache::ClearBackend() {
169 DCHECK_EQ(0U, curr_size_bytes_
);
172 ProgramCache::ProgramLoadResult
MemoryProgramCache::LoadLinkedProgram(
176 const LocationMap
* bind_attrib_location_map
,
177 const ShaderCacheCallback
& shader_callback
) {
178 char a_sha
[kHashLength
];
179 char b_sha
[kHashLength
];
180 DCHECK(shader_a
&& !shader_a
->last_compiled_source().empty() &&
181 shader_b
&& !shader_b
->last_compiled_source().empty());
183 shader_a
->last_compiled_signature(), a_sha
);
185 shader_b
->last_compiled_signature(), b_sha
);
187 char sha
[kHashLength
];
188 ComputeProgramHash(a_sha
,
190 bind_attrib_location_map
,
192 const std::string
sha_string(sha
, kHashLength
);
194 ProgramMRUCache::iterator found
= store_
.Get(sha_string
);
195 if (found
== store_
.end()) {
196 return PROGRAM_LOAD_FAILURE
;
198 const scoped_refptr
<ProgramCacheValue
> value
= found
->second
;
199 glProgramBinary(program
,
201 static_cast<const GLvoid
*>(value
->data()),
204 glGetProgramiv(program
, GL_LINK_STATUS
, &success
);
205 if (success
== GL_FALSE
) {
206 return PROGRAM_LOAD_FAILURE
;
208 shader_a
->set_attrib_map(value
->attrib_map_0());
209 shader_a
->set_uniform_map(value
->uniform_map_0());
210 shader_a
->set_varying_map(value
->varying_map_0());
211 shader_b
->set_attrib_map(value
->attrib_map_1());
212 shader_b
->set_uniform_map(value
->uniform_map_1());
213 shader_b
->set_varying_map(value
->varying_map_1());
215 if (!shader_callback
.is_null() &&
216 !base::CommandLine::ForCurrentProcess()->HasSwitch(
217 switches::kDisableGpuShaderDiskCache
)) {
218 scoped_ptr
<GpuProgramProto
> proto(
219 GpuProgramProto::default_instance().New());
220 proto
->set_sha(sha
, kHashLength
);
221 proto
->set_format(value
->format());
222 proto
->set_program(value
->data(), value
->length());
224 FillShaderProto(proto
->mutable_vertex_shader(), a_sha
, shader_a
);
225 FillShaderProto(proto
->mutable_fragment_shader(), b_sha
, shader_b
);
226 RunShaderCallback(shader_callback
, proto
.get(), sha_string
);
229 return PROGRAM_LOAD_SUCCESS
;
232 void MemoryProgramCache::SaveLinkedProgram(
234 const Shader
* shader_a
,
235 const Shader
* shader_b
,
236 const LocationMap
* bind_attrib_location_map
,
237 const ShaderCacheCallback
& shader_callback
) {
240 glGetProgramiv(program
, GL_PROGRAM_BINARY_LENGTH_OES
, &length
);
241 if (length
== 0 || static_cast<unsigned int>(length
) > max_size_bytes_
) {
244 scoped_ptr
<char[]> binary(new char[length
]);
245 glGetProgramBinary(program
,
250 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.ProgramBinarySizeBytes", length
);
252 char a_sha
[kHashLength
];
253 char b_sha
[kHashLength
];
254 DCHECK(shader_a
&& !shader_a
->last_compiled_source().empty() &&
255 shader_b
&& !shader_b
->last_compiled_source().empty());
257 shader_a
->last_compiled_signature(), a_sha
);
259 shader_b
->last_compiled_signature(), b_sha
);
261 char sha
[kHashLength
];
262 ComputeProgramHash(a_sha
,
264 bind_attrib_location_map
,
266 const std::string
sha_string(sha
, sizeof(sha
));
268 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeBeforeKb",
269 curr_size_bytes_
/ 1024);
271 // Evict any cached program with the same key in favor of the least recently
273 ProgramMRUCache::iterator existing
= store_
.Peek(sha_string
);
274 if(existing
!= store_
.end())
275 store_
.Erase(existing
);
277 while (curr_size_bytes_
+ length
> max_size_bytes_
) {
278 DCHECK(!store_
.empty());
279 store_
.Erase(store_
.rbegin());
282 if (!shader_callback
.is_null() &&
283 !base::CommandLine::ForCurrentProcess()->HasSwitch(
284 switches::kDisableGpuShaderDiskCache
)) {
285 scoped_ptr
<GpuProgramProto
> proto(
286 GpuProgramProto::default_instance().New());
287 proto
->set_sha(sha
, kHashLength
);
288 proto
->set_format(format
);
289 proto
->set_program(binary
.get(), length
);
291 FillShaderProto(proto
->mutable_vertex_shader(), a_sha
, shader_a
);
292 FillShaderProto(proto
->mutable_fragment_shader(), b_sha
, shader_b
);
293 RunShaderCallback(shader_callback
, proto
.get(), sha_string
);
296 store_
.Put(sha_string
,
297 new ProgramCacheValue(length
,
302 shader_a
->attrib_map(),
303 shader_a
->uniform_map(),
304 shader_a
->varying_map(),
306 shader_b
->attrib_map(),
307 shader_b
->uniform_map(),
308 shader_b
->varying_map(),
311 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeAfterKb",
312 curr_size_bytes_
/ 1024);
315 void MemoryProgramCache::LoadProgram(const std::string
& program
) {
316 scoped_ptr
<GpuProgramProto
> proto(GpuProgramProto::default_instance().New());
317 if (proto
->ParseFromString(program
)) {
318 AttributeMap vertex_attribs
;
319 UniformMap vertex_uniforms
;
320 VaryingMap vertex_varyings
;
321 for (int i
= 0; i
< proto
->vertex_shader().attribs_size(); i
++) {
322 RetrieveShaderAttributeInfo(proto
->vertex_shader().attribs(i
),
325 for (int i
= 0; i
< proto
->vertex_shader().uniforms_size(); i
++) {
326 RetrieveShaderUniformInfo(proto
->vertex_shader().uniforms(i
),
329 for (int i
= 0; i
< proto
->vertex_shader().varyings_size(); i
++) {
330 RetrieveShaderVaryingInfo(proto
->vertex_shader().varyings(i
),
334 AttributeMap fragment_attribs
;
335 UniformMap fragment_uniforms
;
336 VaryingMap fragment_varyings
;
337 for (int i
= 0; i
< proto
->fragment_shader().attribs_size(); i
++) {
338 RetrieveShaderAttributeInfo(proto
->fragment_shader().attribs(i
),
341 for (int i
= 0; i
< proto
->fragment_shader().uniforms_size(); i
++) {
342 RetrieveShaderUniformInfo(proto
->fragment_shader().uniforms(i
),
345 for (int i
= 0; i
< proto
->fragment_shader().varyings_size(); i
++) {
346 RetrieveShaderVaryingInfo(proto
->fragment_shader().varyings(i
),
350 scoped_ptr
<char[]> binary(new char[proto
->program().length()]);
351 memcpy(binary
.get(), proto
->program().c_str(), proto
->program().length());
353 store_
.Put(proto
->sha(),
354 new ProgramCacheValue(proto
->program().length(),
358 proto
->vertex_shader().sha().c_str(),
362 proto
->fragment_shader().sha().c_str(),
368 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeAfterKb",
369 curr_size_bytes_
/ 1024);
371 LOG(ERROR
) << "Failed to parse proto file.";
375 MemoryProgramCache::ProgramCacheValue::ProgramCacheValue(
379 const std::string
& program_hash
,
380 const char* shader_0_hash
,
381 const AttributeMap
& attrib_map_0
,
382 const UniformMap
& uniform_map_0
,
383 const VaryingMap
& varying_map_0
,
384 const char* shader_1_hash
,
385 const AttributeMap
& attrib_map_1
,
386 const UniformMap
& uniform_map_1
,
387 const VaryingMap
& varying_map_1
,
388 MemoryProgramCache
* program_cache
)
392 program_hash_(program_hash
),
393 shader_0_hash_(shader_0_hash
, kHashLength
),
394 attrib_map_0_(attrib_map_0
),
395 uniform_map_0_(uniform_map_0
),
396 varying_map_0_(varying_map_0
),
397 shader_1_hash_(shader_1_hash
, kHashLength
),
398 attrib_map_1_(attrib_map_1
),
399 uniform_map_1_(uniform_map_1
),
400 varying_map_1_(varying_map_1
),
401 program_cache_(program_cache
) {
402 program_cache_
->curr_size_bytes_
+= length_
;
403 program_cache_
->LinkedProgramCacheSuccess(program_hash
);
406 MemoryProgramCache::ProgramCacheValue::~ProgramCacheValue() {
407 program_cache_
->curr_size_bytes_
-= length_
;
408 program_cache_
->Evict(program_hash_
);