blob: e09d132f6a05611187ae8bd73ba3d8d92166d10e [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// 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.
4
5#include "gpu/command_buffer/service/memory_program_cache.h"
6
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00007#include "base/base64.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +00008#include "base/command_line.h"
9#include "base/metrics/histogram.h"
10#include "base/sha1.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010011#include "base/strings/string_number_conversions.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000012#include "gpu/command_buffer/common/constants.h"
13#include "gpu/command_buffer/service/disk_cache_proto.pb.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000014#include "gpu/command_buffer/service/gl_utils.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000015#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000016#include "gpu/command_buffer/service/gpu_switches.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000017#include "gpu/command_buffer/service/shader_manager.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000018#include "ui/gl/gl_bindings.h"
19
20namespace {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000021
Torne (Richard Coles)58218062012-11-14 11:43:16 +000022size_t GetCacheSizeBytes() {
23 size_t size;
24 const CommandLine* command_line = CommandLine::ForCurrentProcess();
25 if (command_line->HasSwitch(switches::kGpuProgramCacheSizeKb) &&
26 base::StringToSizeT(command_line->GetSwitchValueNative(
27 switches::kGpuProgramCacheSizeKb),
28 &size)) {
29 return size * 1024;
30 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000031 return gpu::kDefaultMaxProgramCacheMemoryBytes;
Torne (Richard Coles)58218062012-11-14 11:43:16 +000032}
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000033
Torne (Richard Coles)58218062012-11-14 11:43:16 +000034} // anonymous namespace
35
36namespace gpu {
37namespace gles2 {
38
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000039namespace {
40
41enum ShaderMapType {
42 ATTRIB_MAP = 0,
43 UNIFORM_MAP
44};
45
46void StoreShaderInfo(ShaderMapType type, ShaderProto *proto,
47 const ShaderTranslator::VariableMap& map) {
48 ShaderTranslator::VariableMap::const_iterator iter;
49 for (iter = map.begin(); iter != map.end(); iter++) {
50 ShaderInfoProto* info;
51 if (type == UNIFORM_MAP) {
52 info = proto->add_uniforms();
53 } else {
54 info = proto->add_attribs();
55 }
56
57 info->set_key(iter->first);
58 info->set_type(iter->second.type);
59 info->set_size(iter->second.size);
60 info->set_name(iter->second.name);
61 }
62}
63
64void RetrieveShaderInfo(const ShaderInfoProto& proto,
65 ShaderTranslator::VariableMap* map) {
66 ShaderTranslator::VariableInfo info(proto.type(), proto.size(),
67 proto.name());
68 (*map)[proto.key()] = info;
69}
70
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010071void FillShaderProto(ShaderProto* proto, const char* sha,
72 const Shader* shader) {
73 proto->set_sha(sha, gpu::gles2::ProgramCache::kHashLength);
74 StoreShaderInfo(ATTRIB_MAP, proto, shader->attrib_map());
75 StoreShaderInfo(UNIFORM_MAP, proto, shader->uniform_map());
76}
77
78void RunShaderCallback(const ShaderCacheCallback& callback,
79 GpuProgramProto* proto,
80 std::string sha_string) {
81 std::string shader;
82 proto->SerializeToString(&shader);
83
84 std::string key;
85 base::Base64Encode(sha_string, &key);
86 callback.Run(key, shader);
87}
88
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000089} // namespace
90
Torne (Richard Coles)58218062012-11-14 11:43:16 +000091MemoryProgramCache::MemoryProgramCache()
92 : max_size_bytes_(GetCacheSizeBytes()),
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010093 curr_size_bytes_(0),
94 store_(ProgramMRUCache::NO_AUTO_EVICT) {
95}
Torne (Richard Coles)58218062012-11-14 11:43:16 +000096
97MemoryProgramCache::MemoryProgramCache(const size_t max_cache_size_bytes)
98 : max_size_bytes_(max_cache_size_bytes),
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010099 curr_size_bytes_(0),
100 store_(ProgramMRUCache::NO_AUTO_EVICT) {
101}
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000102
103MemoryProgramCache::~MemoryProgramCache() {}
104
105void MemoryProgramCache::ClearBackend() {
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100106 store_.Clear();
107 DCHECK_EQ(0U, curr_size_bytes_);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000108}
109
110ProgramCache::ProgramLoadResult MemoryProgramCache::LoadLinkedProgram(
111 GLuint program,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000112 Shader* shader_a,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100113 const ShaderTranslatorInterface* translator_a,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000114 Shader* shader_b,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100115 const ShaderTranslatorInterface* translator_b,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100116 const LocationMap* bind_attrib_location_map,
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100117 const ShaderCacheCallback& shader_callback) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000118 char a_sha[kHashLength];
119 char b_sha[kHashLength];
Ben Murdochbb1529c2013-08-08 10:24:53 +0100120 DCHECK(shader_a && shader_a->signature_source() &&
121 shader_b && shader_b->signature_source());
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100122 ComputeShaderHash(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100123 *shader_a->signature_source(), translator_a, a_sha);
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100124 ComputeShaderHash(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100125 *shader_b->signature_source(), translator_b, b_sha);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000126
127 char sha[kHashLength];
128 ComputeProgramHash(a_sha,
129 b_sha,
130 bind_attrib_location_map,
131 sha);
132 const std::string sha_string(sha, kHashLength);
133
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100134 ProgramMRUCache::iterator found = store_.Get(sha_string);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000135 if (found == store_.end()) {
136 return PROGRAM_LOAD_FAILURE;
137 }
138 const scoped_refptr<ProgramCacheValue> value = found->second;
139 glProgramBinary(program,
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100140 value->format(),
141 static_cast<const GLvoid*>(value->data()),
142 value->length());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000143 GLint success = 0;
144 glGetProgramiv(program, GL_LINK_STATUS, &success);
145 if (success == GL_FALSE) {
146 return PROGRAM_LOAD_FAILURE;
147 }
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100148 shader_a->set_attrib_map(value->attrib_map_0());
149 shader_a->set_uniform_map(value->uniform_map_0());
150 shader_b->set_attrib_map(value->attrib_map_1());
151 shader_b->set_uniform_map(value->uniform_map_1());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100152
153 if (!shader_callback.is_null() &&
154 !CommandLine::ForCurrentProcess()->HasSwitch(
155 switches::kDisableGpuShaderDiskCache)) {
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100156 scoped_ptr<GpuProgramProto> proto(
157 GpuProgramProto::default_instance().New());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100158 proto->set_sha(sha, kHashLength);
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100159 proto->set_format(value->format());
160 proto->set_program(value->data(), value->length());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100161
162 FillShaderProto(proto->mutable_vertex_shader(), a_sha, shader_a);
163 FillShaderProto(proto->mutable_fragment_shader(), b_sha, shader_b);
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100164 RunShaderCallback(shader_callback, proto.get(), sha_string);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100165 }
166
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000167 return PROGRAM_LOAD_SUCCESS;
168}
169
170void MemoryProgramCache::SaveLinkedProgram(
171 GLuint program,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000172 const Shader* shader_a,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100173 const ShaderTranslatorInterface* translator_a,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000174 const Shader* shader_b,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100175 const ShaderTranslatorInterface* translator_b,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000176 const LocationMap* bind_attrib_location_map,
177 const ShaderCacheCallback& shader_callback) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000178 GLenum format;
179 GLsizei length = 0;
180 glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH_OES, &length);
181 if (length == 0 || static_cast<unsigned int>(length) > max_size_bytes_) {
182 return;
183 }
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100184 scoped_ptr<char[]> binary(new char[length]);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000185 glGetProgramBinary(program,
186 length,
187 NULL,
188 &format,
189 binary.get());
190 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.ProgramBinarySizeBytes", length);
191
192 char a_sha[kHashLength];
193 char b_sha[kHashLength];
Ben Murdochbb1529c2013-08-08 10:24:53 +0100194 DCHECK(shader_a && shader_a->signature_source() &&
195 shader_b && shader_b->signature_source());
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100196 ComputeShaderHash(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100197 *shader_a->signature_source(), translator_a, a_sha);
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100198 ComputeShaderHash(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100199 *shader_b->signature_source(), translator_b, b_sha);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000200
201 char sha[kHashLength];
202 ComputeProgramHash(a_sha,
203 b_sha,
204 bind_attrib_location_map,
205 sha);
206 const std::string sha_string(sha, sizeof(sha));
207
208 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeBeforeKb",
209 curr_size_bytes_ / 1024);
210
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100211 // Evict any cached program with the same key in favor of the least recently
212 // accessed.
213 ProgramMRUCache::iterator existing = store_.Peek(sha_string);
214 if(existing != store_.end())
215 store_.Erase(existing);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000216
217 while (curr_size_bytes_ + length > max_size_bytes_) {
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100218 DCHECK(!store_.empty());
219 store_.Erase(store_.rbegin());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000220 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000221
222 if (!shader_callback.is_null() &&
223 !CommandLine::ForCurrentProcess()->HasSwitch(
224 switches::kDisableGpuShaderDiskCache)) {
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100225 scoped_ptr<GpuProgramProto> proto(
226 GpuProgramProto::default_instance().New());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000227 proto->set_sha(sha, kHashLength);
228 proto->set_format(format);
229 proto->set_program(binary.get(), length);
230
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100231 FillShaderProto(proto->mutable_vertex_shader(), a_sha, shader_a);
232 FillShaderProto(proto->mutable_fragment_shader(), b_sha, shader_b);
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100233 RunShaderCallback(shader_callback, proto.get(), sha_string);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000234 }
235
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100236 store_.Put(sha_string,
237 new ProgramCacheValue(length,
238 format,
239 binary.release(),
240 sha_string,
241 a_sha,
242 shader_a->attrib_map(),
243 shader_a->uniform_map(),
244 b_sha,
245 shader_b->attrib_map(),
246 shader_b->uniform_map(),
247 this));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000248
249 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeAfterKb",
Ben Murdochbb1529c2013-08-08 10:24:53 +0100250 curr_size_bytes_ / 1024);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000251}
252
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000253void MemoryProgramCache::LoadProgram(const std::string& program) {
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100254 scoped_ptr<GpuProgramProto> proto(GpuProgramProto::default_instance().New());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000255 if (proto->ParseFromString(program)) {
256 ShaderTranslator::VariableMap vertex_attribs;
257 ShaderTranslator::VariableMap vertex_uniforms;
258
259 for (int i = 0; i < proto->vertex_shader().attribs_size(); i++) {
260 RetrieveShaderInfo(proto->vertex_shader().attribs(i), &vertex_attribs);
261 }
262
263 for (int i = 0; i < proto->vertex_shader().uniforms_size(); i++) {
264 RetrieveShaderInfo(proto->vertex_shader().uniforms(i), &vertex_uniforms);
265 }
266
267 ShaderTranslator::VariableMap fragment_attribs;
268 ShaderTranslator::VariableMap fragment_uniforms;
269
270 for (int i = 0; i < proto->fragment_shader().attribs_size(); i++) {
271 RetrieveShaderInfo(proto->fragment_shader().attribs(i),
272 &fragment_attribs);
273 }
274
275 for (int i = 0; i < proto->fragment_shader().uniforms_size(); i++) {
276 RetrieveShaderInfo(proto->fragment_shader().uniforms(i),
277 &fragment_uniforms);
278 }
279
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100280 scoped_ptr<char[]> binary(new char[proto->program().length()]);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000281 memcpy(binary.get(), proto->program().c_str(), proto->program().length());
282
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100283 store_.Put(proto->sha(),
284 new ProgramCacheValue(proto->program().length(),
285 proto->format(),
286 binary.release(),
287 proto->sha(),
288 proto->vertex_shader().sha().c_str(),
289 vertex_attribs,
290 vertex_uniforms,
291 proto->fragment_shader().sha().c_str(),
292 fragment_attribs,
293 fragment_uniforms,
294 this));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000295
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000296 UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeAfterKb",
297 curr_size_bytes_ / 1024);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000298 } else {
299 LOG(ERROR) << "Failed to parse proto file.";
300 }
301}
302
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000303MemoryProgramCache::ProgramCacheValue::ProgramCacheValue(
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100304 GLsizei length,
305 GLenum format,
306 const char* data,
307 const std::string& program_hash,
308 const char* shader_0_hash,
309 const ShaderTranslator::VariableMap& attrib_map_0,
310 const ShaderTranslator::VariableMap& uniform_map_0,
311 const char* shader_1_hash,
312 const ShaderTranslator::VariableMap& attrib_map_1,
313 const ShaderTranslator::VariableMap& uniform_map_1,
314 MemoryProgramCache* program_cache)
315 : length_(length),
316 format_(format),
317 data_(data),
318 program_hash_(program_hash),
319 shader_0_hash_(shader_0_hash, kHashLength),
320 attrib_map_0_(attrib_map_0),
321 uniform_map_0_(uniform_map_0),
322 shader_1_hash_(shader_1_hash, kHashLength),
323 attrib_map_1_(attrib_map_1),
324 uniform_map_1_(uniform_map_1),
325 program_cache_(program_cache) {
326 program_cache_->curr_size_bytes_ += length_;
Ben Murdochbb1529c2013-08-08 10:24:53 +0100327 program_cache_->LinkedProgramCacheSuccess(program_hash);
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100328}
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000329
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100330MemoryProgramCache::ProgramCacheValue::~ProgramCacheValue() {
331 program_cache_->curr_size_bytes_ -= length_;
Ben Murdochbb1529c2013-08-08 10:24:53 +0100332 program_cache_->Evict(program_hash_);
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100333}
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000334
335} // namespace gles2
336} // namespace gpu