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 "gpu/command_buffer/service/shader_translator.h"
19 #include "ui/gl/gl_bindings.h"
23 size_t GetCacheSizeBytes() {
24 const CommandLine
* command_line
= 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 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
;
55 info
= proto
->add_uniforms();
58 info
= proto
->add_attribs();
61 info
= proto
->add_varyings();
63 default: NOTREACHED();
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
);
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
;
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());
91 void RunShaderCallback(const ShaderCacheCallback
& callback
,
92 GpuProgramProto
* proto
,
93 std::string sha_string
) {
95 proto
->SerializeToString(&shader
);
98 base::Base64Encode(sha_string
, &key
);
99 callback
.Run(key
, shader
);
104 MemoryProgramCache::MemoryProgramCache()
105 : max_size_bytes_(GetCacheSizeBytes()),
107 store_(ProgramMRUCache::NO_AUTO_EVICT
) {
110 MemoryProgramCache::MemoryProgramCache(const size_t max_cache_size_bytes
)
111 : max_size_bytes_(max_cache_size_bytes
),
113 store_(ProgramMRUCache::NO_AUTO_EVICT
) {
116 MemoryProgramCache::~MemoryProgramCache() {}
118 void MemoryProgramCache::ClearBackend() {
120 DCHECK_EQ(0U, curr_size_bytes_
);
123 ProgramCache::ProgramLoadResult
MemoryProgramCache::LoadLinkedProgram(
126 const ShaderTranslatorInterface
* translator_a
,
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().empty() &&
134 shader_b
&& !shader_b
->signature_source().empty());
136 shader_a
->signature_source(), translator_a
, a_sha
);
138 shader_b
->signature_source(), translator_b
, b_sha
);
140 char sha
[kHashLength
];
141 ComputeProgramHash(a_sha
,
143 bind_attrib_location_map
,
145 const std::string
sha_string(sha
, kHashLength
);
147 ProgramMRUCache::iterator found
= store_
.Get(sha_string
);
148 if (found
== store_
.end()) {
149 return PROGRAM_LOAD_FAILURE
;
151 const scoped_refptr
<ProgramCacheValue
> value
= found
->second
;
152 glProgramBinary(program
,
154 static_cast<const GLvoid
*>(value
->data()),
157 glGetProgramiv(program
, GL_LINK_STATUS
, &success
);
158 if (success
== GL_FALSE
) {
159 return PROGRAM_LOAD_FAILURE
;
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());
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());
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
);
182 return PROGRAM_LOAD_SUCCESS
;
185 void MemoryProgramCache::SaveLinkedProgram(
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
) {
195 glGetProgramiv(program
, GL_PROGRAM_BINARY_LENGTH_OES
, &length
);
196 if (length
== 0 || static_cast<unsigned int>(length
) > max_size_bytes_
) {
199 scoped_ptr
<char[]> binary(new char[length
]);
200 glGetProgramBinary(program
,
205 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.ProgramBinarySizeBytes", length
);
207 char a_sha
[kHashLength
];
208 char b_sha
[kHashLength
];
209 DCHECK(shader_a
&& !shader_a
->signature_source().empty() &&
210 shader_b
&& !shader_b
->signature_source().empty());
212 shader_a
->signature_source(), translator_a
, a_sha
);
214 shader_b
->signature_source(), translator_b
, b_sha
);
216 char sha
[kHashLength
];
217 ComputeProgramHash(a_sha
,
219 bind_attrib_location_map
,
221 const std::string
sha_string(sha
, sizeof(sha
));
223 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeBeforeKb",
224 curr_size_bytes_
/ 1024);
226 // Evict any cached program with the same key in favor of the least recently
228 ProgramMRUCache::iterator existing
= store_
.Peek(sha_string
);
229 if(existing
!= store_
.end())
230 store_
.Erase(existing
);
232 while (curr_size_bytes_
+ length
> max_size_bytes_
) {
233 DCHECK(!store_
.empty());
234 store_
.Erase(store_
.rbegin());
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
);
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
);
251 store_
.Put(sha_string
,
252 new ProgramCacheValue(length
,
257 shader_a
->attrib_map(),
258 shader_a
->uniform_map(),
259 shader_a
->varying_map(),
261 shader_b
->attrib_map(),
262 shader_b
->uniform_map(),
263 shader_b
->varying_map(),
266 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeAfterKb",
267 curr_size_bytes_
/ 1024);
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
;
277 for (int i
= 0; i
< proto
->vertex_shader().attribs_size(); i
++) {
278 RetrieveShaderInfo(proto
->vertex_shader().attribs(i
), &vertex_attribs
);
281 for (int i
= 0; i
< proto
->vertex_shader().uniforms_size(); i
++) {
282 RetrieveShaderInfo(proto
->vertex_shader().uniforms(i
), &vertex_uniforms
);
285 for (int i
= 0; i
< proto
->vertex_shader().varyings_size(); i
++) {
286 RetrieveShaderInfo(proto
->vertex_shader().varyings(i
), &vertex_varyings
);
289 ShaderTranslator::VariableMap fragment_attribs
;
290 ShaderTranslator::VariableMap fragment_uniforms
;
291 ShaderTranslator::VariableMap fragment_varyings
;
293 for (int i
= 0; i
< proto
->fragment_shader().attribs_size(); i
++) {
294 RetrieveShaderInfo(proto
->fragment_shader().attribs(i
),
298 for (int i
= 0; i
< proto
->fragment_shader().uniforms_size(); i
++) {
299 RetrieveShaderInfo(proto
->fragment_shader().uniforms(i
),
303 for (int i
= 0; i
< proto
->fragment_shader().varyings_size(); i
++) {
304 RetrieveShaderInfo(proto
->fragment_shader().varyings(i
),
308 scoped_ptr
<char[]> binary(new char[proto
->program().length()]);
309 memcpy(binary
.get(), proto
->program().c_str(), proto
->program().length());
311 store_
.Put(proto
->sha(),
312 new ProgramCacheValue(proto
->program().length(),
316 proto
->vertex_shader().sha().c_str(),
320 proto
->fragment_shader().sha().c_str(),
326 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeAfterKb",
327 curr_size_bytes_
/ 1024);
329 LOG(ERROR
) << "Failed to parse proto file.";
333 MemoryProgramCache::ProgramCacheValue::ProgramCacheValue(
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
)
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
);
364 MemoryProgramCache::ProgramCacheValue::~ProgramCacheValue() {
365 program_cache_
->curr_size_bytes_
-= length_
;
366 program_cache_
->Evict(program_hash_
);