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"
23 size_t GetCacheSizeBytes() {
24 const base::CommandLine
* command_line
=
25 base::CommandLine::ForCurrentProcess();
26 if (command_line
->HasSwitch(switches::kGpuProgramCacheSizeKb
)) {
28 if (base::StringToSizeT(
29 command_line
->GetSwitchValueNative(switches::kGpuProgramCacheSizeKb
),
33 return gpu::kDefaultMaxProgramCacheMemoryBytes
;
36 } // anonymous namespace
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
) {
127 RetrieveShaderVariableInfo(proto
.basic(), &uniform
);
128 (*map
)[proto
.basic().mapped_name()] = uniform
;
131 void RetrieveShaderVaryingInfo(
132 const ShaderVaryingProto
& proto
, VaryingMap
* map
) {
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
) {
145 proto
->SerializeToString(&shader
);
148 base::Base64Encode(sha_string
, &key
);
149 callback
.Run(key
, shader
);
154 MemoryProgramCache::MemoryProgramCache()
155 : max_size_bytes_(GetCacheSizeBytes()),
157 store_(ProgramMRUCache::NO_AUTO_EVICT
) {
160 MemoryProgramCache::MemoryProgramCache(const size_t max_cache_size_bytes
)
161 : max_size_bytes_(max_cache_size_bytes
),
163 store_(ProgramMRUCache::NO_AUTO_EVICT
) {
166 MemoryProgramCache::~MemoryProgramCache() {}
168 void MemoryProgramCache::ClearBackend() {
170 DCHECK_EQ(0U, curr_size_bytes_
);
173 ProgramCache::ProgramLoadResult
MemoryProgramCache::LoadLinkedProgram(
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());
186 shader_a
->last_compiled_signature(), a_sha
);
188 shader_b
->last_compiled_signature(), b_sha
);
190 char sha
[kHashLength
];
191 ComputeProgramHash(a_sha
,
193 bind_attrib_location_map
,
194 transform_feedback_varyings
,
195 transform_feedback_buffer_mode
,
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
,
206 static_cast<const GLvoid
*>(value
->data()),
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(
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
) {
247 glGetProgramiv(program
, GL_PROGRAM_BINARY_LENGTH_OES
, &length
);
248 if (length
== 0 || static_cast<unsigned int>(length
) > max_size_bytes_
) {
251 scoped_ptr
<char[]> binary(new char[length
]);
252 glGetProgramBinary(program
,
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());
264 shader_a
->last_compiled_signature(), a_sha
);
266 shader_b
->last_compiled_signature(), b_sha
);
268 char sha
[kHashLength
];
269 ComputeProgramHash(a_sha
,
271 bind_attrib_location_map
,
272 transform_feedback_varyings
,
273 transform_feedback_buffer_mode
,
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
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
,
311 shader_a
->attrib_map(),
312 shader_a
->uniform_map(),
313 shader_a
->varying_map(),
315 shader_b
->attrib_map(),
316 shader_b
->uniform_map(),
317 shader_b
->varying_map(),
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
),
334 for (int i
= 0; i
< proto
->vertex_shader().uniforms_size(); i
++) {
335 RetrieveShaderUniformInfo(proto
->vertex_shader().uniforms(i
),
338 for (int i
= 0; i
< proto
->vertex_shader().varyings_size(); i
++) {
339 RetrieveShaderVaryingInfo(proto
->vertex_shader().varyings(i
),
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
),
350 for (int i
= 0; i
< proto
->fragment_shader().uniforms_size(); i
++) {
351 RetrieveShaderUniformInfo(proto
->fragment_shader().uniforms(i
),
354 for (int i
= 0; i
< proto
->fragment_shader().varyings_size(); i
++) {
355 RetrieveShaderVaryingInfo(proto
->fragment_shader().varyings(i
),
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(),
367 proto
->vertex_shader().sha().c_str(),
371 proto
->fragment_shader().sha().c_str(),
377 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeAfterKb",
378 curr_size_bytes_
/ 1024);
380 LOG(ERROR
) << "Failed to parse proto file.";
384 MemoryProgramCache::ProgramCacheValue::ProgramCacheValue(
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
)
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_
);