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 std::vector
<std::string
>& transform_feedback_varyings
,
178 GLenum transform_feedback_buffer_mode
,
179 const ShaderCacheCallback
& shader_callback
) {
180 char a_sha
[kHashLength
];
181 char b_sha
[kHashLength
];
182 DCHECK(shader_a
&& !shader_a
->last_compiled_source().empty() &&
183 shader_b
&& !shader_b
->last_compiled_source().empty());
185 shader_a
->last_compiled_signature(), a_sha
);
187 shader_b
->last_compiled_signature(), b_sha
);
189 char sha
[kHashLength
];
190 ComputeProgramHash(a_sha
,
192 bind_attrib_location_map
,
193 transform_feedback_varyings
,
194 transform_feedback_buffer_mode
,
196 const std::string
sha_string(sha
, kHashLength
);
198 ProgramMRUCache::iterator found
= store_
.Get(sha_string
);
199 if (found
== store_
.end()) {
200 return PROGRAM_LOAD_FAILURE
;
202 const scoped_refptr
<ProgramCacheValue
> value
= found
->second
;
203 glProgramBinary(program
,
205 static_cast<const GLvoid
*>(value
->data()),
208 glGetProgramiv(program
, GL_LINK_STATUS
, &success
);
209 if (success
== GL_FALSE
) {
210 return PROGRAM_LOAD_FAILURE
;
212 shader_a
->set_attrib_map(value
->attrib_map_0());
213 shader_a
->set_uniform_map(value
->uniform_map_0());
214 shader_a
->set_varying_map(value
->varying_map_0());
215 shader_b
->set_attrib_map(value
->attrib_map_1());
216 shader_b
->set_uniform_map(value
->uniform_map_1());
217 shader_b
->set_varying_map(value
->varying_map_1());
219 if (!shader_callback
.is_null() &&
220 !base::CommandLine::ForCurrentProcess()->HasSwitch(
221 switches::kDisableGpuShaderDiskCache
)) {
222 scoped_ptr
<GpuProgramProto
> proto(
223 GpuProgramProto::default_instance().New());
224 proto
->set_sha(sha
, kHashLength
);
225 proto
->set_format(value
->format());
226 proto
->set_program(value
->data(), value
->length());
228 FillShaderProto(proto
->mutable_vertex_shader(), a_sha
, shader_a
);
229 FillShaderProto(proto
->mutable_fragment_shader(), b_sha
, shader_b
);
230 RunShaderCallback(shader_callback
, proto
.get(), sha_string
);
233 return PROGRAM_LOAD_SUCCESS
;
236 void MemoryProgramCache::SaveLinkedProgram(
238 const Shader
* shader_a
,
239 const Shader
* shader_b
,
240 const LocationMap
* bind_attrib_location_map
,
241 const std::vector
<std::string
>& transform_feedback_varyings
,
242 GLenum transform_feedback_buffer_mode
,
243 const ShaderCacheCallback
& shader_callback
) {
246 glGetProgramiv(program
, GL_PROGRAM_BINARY_LENGTH_OES
, &length
);
247 if (length
== 0 || static_cast<unsigned int>(length
) > max_size_bytes_
) {
250 scoped_ptr
<char[]> binary(new char[length
]);
251 glGetProgramBinary(program
,
256 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.ProgramBinarySizeBytes", length
);
258 char a_sha
[kHashLength
];
259 char b_sha
[kHashLength
];
260 DCHECK(shader_a
&& !shader_a
->last_compiled_source().empty() &&
261 shader_b
&& !shader_b
->last_compiled_source().empty());
263 shader_a
->last_compiled_signature(), a_sha
);
265 shader_b
->last_compiled_signature(), b_sha
);
267 char sha
[kHashLength
];
268 ComputeProgramHash(a_sha
,
270 bind_attrib_location_map
,
271 transform_feedback_varyings
,
272 transform_feedback_buffer_mode
,
274 const std::string
sha_string(sha
, sizeof(sha
));
276 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeBeforeKb",
277 curr_size_bytes_
/ 1024);
279 // Evict any cached program with the same key in favor of the least recently
281 ProgramMRUCache::iterator existing
= store_
.Peek(sha_string
);
282 if(existing
!= store_
.end())
283 store_
.Erase(existing
);
285 while (curr_size_bytes_
+ length
> max_size_bytes_
) {
286 DCHECK(!store_
.empty());
287 store_
.Erase(store_
.rbegin());
290 if (!shader_callback
.is_null() &&
291 !base::CommandLine::ForCurrentProcess()->HasSwitch(
292 switches::kDisableGpuShaderDiskCache
)) {
293 scoped_ptr
<GpuProgramProto
> proto(
294 GpuProgramProto::default_instance().New());
295 proto
->set_sha(sha
, kHashLength
);
296 proto
->set_format(format
);
297 proto
->set_program(binary
.get(), length
);
299 FillShaderProto(proto
->mutable_vertex_shader(), a_sha
, shader_a
);
300 FillShaderProto(proto
->mutable_fragment_shader(), b_sha
, shader_b
);
301 RunShaderCallback(shader_callback
, proto
.get(), sha_string
);
304 store_
.Put(sha_string
,
305 new ProgramCacheValue(length
,
310 shader_a
->attrib_map(),
311 shader_a
->uniform_map(),
312 shader_a
->varying_map(),
314 shader_b
->attrib_map(),
315 shader_b
->uniform_map(),
316 shader_b
->varying_map(),
319 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeAfterKb",
320 curr_size_bytes_
/ 1024);
323 void MemoryProgramCache::LoadProgram(const std::string
& program
) {
324 scoped_ptr
<GpuProgramProto
> proto(GpuProgramProto::default_instance().New());
325 if (proto
->ParseFromString(program
)) {
326 AttributeMap vertex_attribs
;
327 UniformMap vertex_uniforms
;
328 VaryingMap vertex_varyings
;
329 for (int i
= 0; i
< proto
->vertex_shader().attribs_size(); i
++) {
330 RetrieveShaderAttributeInfo(proto
->vertex_shader().attribs(i
),
333 for (int i
= 0; i
< proto
->vertex_shader().uniforms_size(); i
++) {
334 RetrieveShaderUniformInfo(proto
->vertex_shader().uniforms(i
),
337 for (int i
= 0; i
< proto
->vertex_shader().varyings_size(); i
++) {
338 RetrieveShaderVaryingInfo(proto
->vertex_shader().varyings(i
),
342 AttributeMap fragment_attribs
;
343 UniformMap fragment_uniforms
;
344 VaryingMap fragment_varyings
;
345 for (int i
= 0; i
< proto
->fragment_shader().attribs_size(); i
++) {
346 RetrieveShaderAttributeInfo(proto
->fragment_shader().attribs(i
),
349 for (int i
= 0; i
< proto
->fragment_shader().uniforms_size(); i
++) {
350 RetrieveShaderUniformInfo(proto
->fragment_shader().uniforms(i
),
353 for (int i
= 0; i
< proto
->fragment_shader().varyings_size(); i
++) {
354 RetrieveShaderVaryingInfo(proto
->fragment_shader().varyings(i
),
358 scoped_ptr
<char[]> binary(new char[proto
->program().length()]);
359 memcpy(binary
.get(), proto
->program().c_str(), proto
->program().length());
361 store_
.Put(proto
->sha(),
362 new ProgramCacheValue(proto
->program().length(),
366 proto
->vertex_shader().sha().c_str(),
370 proto
->fragment_shader().sha().c_str(),
376 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeAfterKb",
377 curr_size_bytes_
/ 1024);
379 LOG(ERROR
) << "Failed to parse proto file.";
383 MemoryProgramCache::ProgramCacheValue::ProgramCacheValue(
387 const std::string
& program_hash
,
388 const char* shader_0_hash
,
389 const AttributeMap
& attrib_map_0
,
390 const UniformMap
& uniform_map_0
,
391 const VaryingMap
& varying_map_0
,
392 const char* shader_1_hash
,
393 const AttributeMap
& attrib_map_1
,
394 const UniformMap
& uniform_map_1
,
395 const VaryingMap
& varying_map_1
,
396 MemoryProgramCache
* program_cache
)
400 program_hash_(program_hash
),
401 shader_0_hash_(shader_0_hash
, kHashLength
),
402 attrib_map_0_(attrib_map_0
),
403 uniform_map_0_(uniform_map_0
),
404 varying_map_0_(varying_map_0
),
405 shader_1_hash_(shader_1_hash
, kHashLength
),
406 attrib_map_1_(attrib_map_1
),
407 uniform_map_1_(uniform_map_1
),
408 varying_map_1_(varying_map_1
),
409 program_cache_(program_cache
) {
410 program_cache_
->curr_size_bytes_
+= length_
;
411 program_cache_
->LinkedProgramCacheSuccess(program_hash
);
414 MemoryProgramCache::ProgramCacheValue::~ProgramCacheValue() {
415 program_cache_
->curr_size_bytes_
-= length_
;
416 program_cache_
->Evict(program_hash_
);