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"
8 #include "gpu/command_buffer/common/gles2_cmd_format.h"
9 #include "gpu/command_buffer/service/gl_utils.h"
10 #include "gpu/command_buffer/service/shader_manager.h"
11 #include "gpu/command_buffer/service/shader_translator.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "ui/gl/gl_bindings.h"
14 #include "ui/gl/gl_mock.h"
17 using ::testing::ElementsAreArray;
18 using ::testing::Invoke;
19 using ::testing::SetArgPointee;
20 using ::testing::SetArrayArgument;
23 typedef gpu::gles2::ShaderTranslator::VariableMap VariableMap;
24 } // anonymous namespace
29 class ProgramBinaryEmulator {
31 ProgramBinaryEmulator(GLsizei length,
38 void GetProgramBinary(GLuint program,
47 memcpy(binary, binary_, length_);
50 void ProgramBinary(GLuint program,
54 // format and length are verified by matcher
55 EXPECT_EQ(0, memcmp(binary_, binary, length));
58 GLsizei length() const { return length_; }
59 GLenum format() const { return format_; }
60 const char* binary() const { return binary_; }
68 class MemoryProgramCacheTest : public testing::Test {
70 static const size_t kCacheSizeBytes = 1024;
71 static const GLuint kVertexShaderClientId = 90;
72 static const GLuint kVertexShaderServiceId = 100;
73 static const GLuint kFragmentShaderClientId = 91;
74 static const GLuint kFragmentShaderServiceId = 100;
76 MemoryProgramCacheTest()
77 : cache_(new MemoryProgramCache(kCacheSizeBytes)),
79 fragment_shader_(NULL),
80 shader_cache_count_(0) { }
81 virtual ~MemoryProgramCacheTest() {
82 shader_manager_.Destroy(false);
85 void ShaderCacheCb(const std::string& key, const std::string& shader) {
86 shader_cache_count_++;
87 shader_cache_shader_ = shader;
90 int32 shader_cache_count() { return shader_cache_count_; }
91 const std::string& shader_cache_shader() { return shader_cache_shader_; }
94 virtual void SetUp() {
95 gl_.reset(new ::testing::StrictMock<gfx::MockGLInterface>());
96 ::gfx::GLInterface::SetGLInterface(gl_.get());
98 vertex_shader_ = shader_manager_.CreateShader(kVertexShaderClientId,
99 kVertexShaderServiceId,
101 fragment_shader_ = shader_manager_.CreateShader(
102 kFragmentShaderClientId,
103 kFragmentShaderServiceId,
105 ASSERT_TRUE(vertex_shader_ != NULL);
106 ASSERT_TRUE(fragment_shader_ != NULL);
107 typedef ShaderTranslatorInterface::VariableInfo VariableInfo;
108 typedef ShaderTranslator::VariableMap VariableMap;
109 VariableMap vertex_attrib_map;
110 VariableMap vertex_uniform_map;
111 VariableMap vertex_varying_map;
112 VariableMap fragment_attrib_map;
113 VariableMap fragment_uniform_map;
114 VariableMap fragment_varying_map;
116 vertex_attrib_map["a"] = VariableInfo(1, 34, SH_PRECISION_LOWP, 0, "a");
117 vertex_uniform_map["a"] = VariableInfo(0, 10, SH_PRECISION_MEDIUMP, 1, "a");
118 vertex_uniform_map["b"] = VariableInfo(2, 3114, SH_PRECISION_HIGHP, 1, "b");
119 vertex_varying_map["c"] = VariableInfo(3, 2, SH_PRECISION_HIGHP, 1, "c");
120 fragment_attrib_map["jjjbb"] =
121 VariableInfo(463, 1114, SH_PRECISION_MEDIUMP, 0, "jjjbb");
122 fragment_uniform_map["k"] =
123 VariableInfo(10, 34413, SH_PRECISION_MEDIUMP, 1, "k");
124 fragment_varying_map["c"] = VariableInfo(3, 2, SH_PRECISION_HIGHP, 1, "c");
126 vertex_shader_->set_attrib_map(vertex_attrib_map);
127 vertex_shader_->set_uniform_map(vertex_uniform_map);
128 vertex_shader_->set_varying_map(vertex_varying_map);
129 fragment_shader_->set_attrib_map(vertex_attrib_map);
130 fragment_shader_->set_uniform_map(vertex_uniform_map);
131 fragment_shader_->set_varying_map(vertex_varying_map);
133 vertex_shader_->UpdateSource("bbbalsldkdkdkd");
134 fragment_shader_->UpdateSource("bbbal sldkdkdkas 134 ad");
136 vertex_shader_->SetStatus(true, NULL, NULL);
137 fragment_shader_->SetStatus(true, NULL, NULL);
140 virtual void TearDown() {
141 ::gfx::GLInterface::SetGLInterface(NULL);
145 void SetExpectationsForSaveLinkedProgram(
146 const GLint program_id,
147 ProgramBinaryEmulator* emulator) const {
148 EXPECT_CALL(*gl_.get(),
149 GetProgramiv(program_id, GL_PROGRAM_BINARY_LENGTH_OES, _))
150 .WillOnce(SetArgPointee<2>(emulator->length()));
151 EXPECT_CALL(*gl_.get(),
152 GetProgramBinary(program_id, emulator->length(), _, _, _))
153 .WillOnce(Invoke(emulator, &ProgramBinaryEmulator::GetProgramBinary));
156 void SetExpectationsForLoadLinkedProgram(
157 const GLint program_id,
158 ProgramBinaryEmulator* emulator) const {
159 EXPECT_CALL(*gl_.get(),
160 ProgramBinary(program_id,
164 .WillOnce(Invoke(emulator, &ProgramBinaryEmulator::ProgramBinary));
165 EXPECT_CALL(*gl_.get(),
166 GetProgramiv(program_id, GL_LINK_STATUS, _))
167 .WillOnce(SetArgPointee<2>(GL_TRUE));
170 void SetExpectationsForLoadLinkedProgramFailure(
171 const GLint program_id,
172 ProgramBinaryEmulator* emulator) const {
173 EXPECT_CALL(*gl_.get(),
174 ProgramBinary(program_id,
178 .WillOnce(Invoke(emulator, &ProgramBinaryEmulator::ProgramBinary));
179 EXPECT_CALL(*gl_.get(),
180 GetProgramiv(program_id, GL_LINK_STATUS, _))
181 .WillOnce(SetArgPointee<2>(GL_FALSE));
184 // Use StrictMock to make 100% sure we know how GL will be called.
185 scoped_ptr< ::testing::StrictMock<gfx::MockGLInterface> > gl_;
186 scoped_ptr<MemoryProgramCache> cache_;
187 ShaderManager shader_manager_;
188 Shader* vertex_shader_;
189 Shader* fragment_shader_;
190 int32 shader_cache_count_;
191 std::string shader_cache_shader_;
194 TEST_F(MemoryProgramCacheTest, CacheSave) {
195 const GLenum kFormat = 1;
196 const int kProgramId = 10;
197 const int kBinaryLength = 20;
198 char test_binary[kBinaryLength];
199 for (int i = 0; i < kBinaryLength; ++i) {
202 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
204 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
205 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
206 fragment_shader_, NULL, NULL,
207 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
208 base::Unretained(this)));
210 EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
211 *vertex_shader_->signature_source(),
213 *fragment_shader_->signature_source(),
216 EXPECT_EQ(1, shader_cache_count());
219 TEST_F(MemoryProgramCacheTest, LoadProgram) {
220 const GLenum kFormat = 1;
221 const int kProgramId = 10;
222 const int kBinaryLength = 20;
223 char test_binary[kBinaryLength];
224 for (int i = 0; i < kBinaryLength; ++i) {
227 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
229 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
230 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
231 fragment_shader_, NULL, NULL,
232 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
233 base::Unretained(this)));
235 EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
236 *vertex_shader_->signature_source(),
238 *fragment_shader_->signature_source(),
241 EXPECT_EQ(1, shader_cache_count());
245 cache_->LoadProgram(shader_cache_shader());
246 EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
247 *vertex_shader_->signature_source(),
249 *fragment_shader_->signature_source(),
254 TEST_F(MemoryProgramCacheTest, CacheLoadMatchesSave) {
255 const GLenum kFormat = 1;
256 const int kProgramId = 10;
257 const int kBinaryLength = 20;
258 char test_binary[kBinaryLength];
259 for (int i = 0; i < kBinaryLength; ++i) {
262 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
264 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
265 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
266 fragment_shader_, NULL, NULL,
267 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
268 base::Unretained(this)));
269 EXPECT_EQ(1, shader_cache_count());
271 VariableMap vertex_attrib_map = vertex_shader_->attrib_map();
272 VariableMap vertex_uniform_map = vertex_shader_->uniform_map();
273 VariableMap vertex_varying_map = vertex_shader_->varying_map();
274 VariableMap fragment_attrib_map = fragment_shader_->attrib_map();
275 VariableMap fragment_uniform_map = fragment_shader_->uniform_map();
276 VariableMap fragment_varying_map = fragment_shader_->varying_map();
278 vertex_shader_->set_attrib_map(VariableMap());
279 vertex_shader_->set_uniform_map(VariableMap());
280 vertex_shader_->set_varying_map(VariableMap());
281 fragment_shader_->set_attrib_map(VariableMap());
282 fragment_shader_->set_uniform_map(VariableMap());
283 fragment_shader_->set_varying_map(VariableMap());
285 SetExpectationsForLoadLinkedProgram(kProgramId, &emulator);
287 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram(
294 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
295 base::Unretained(this))));
297 // apparently the hash_map implementation on android doesn't have the
299 #if !defined(OS_ANDROID)
300 EXPECT_EQ(vertex_attrib_map, vertex_shader_->attrib_map());
301 EXPECT_EQ(vertex_uniform_map, vertex_shader_->uniform_map());
302 EXPECT_EQ(vertex_varying_map, vertex_shader_->varying_map());
303 EXPECT_EQ(fragment_attrib_map, fragment_shader_->attrib_map());
304 EXPECT_EQ(fragment_uniform_map, fragment_shader_->uniform_map());
305 EXPECT_EQ(fragment_varying_map, fragment_shader_->varying_map());
309 TEST_F(MemoryProgramCacheTest, LoadProgramMatchesSave) {
310 const GLenum kFormat = 1;
311 const int kProgramId = 10;
312 const int kBinaryLength = 20;
313 char test_binary[kBinaryLength];
314 for (int i = 0; i < kBinaryLength; ++i) {
317 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
319 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
320 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
321 fragment_shader_, NULL, NULL,
322 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
323 base::Unretained(this)));
324 EXPECT_EQ(1, shader_cache_count());
326 VariableMap vertex_attrib_map = vertex_shader_->attrib_map();
327 VariableMap vertex_uniform_map = vertex_shader_->uniform_map();
328 VariableMap vertex_varying_map = vertex_shader_->varying_map();
329 VariableMap fragment_attrib_map = fragment_shader_->attrib_map();
330 VariableMap fragment_uniform_map = fragment_shader_->uniform_map();
331 VariableMap fragment_varying_map = fragment_shader_->varying_map();
333 vertex_shader_->set_attrib_map(VariableMap());
334 vertex_shader_->set_uniform_map(VariableMap());
335 vertex_shader_->set_varying_map(VariableMap());
336 fragment_shader_->set_attrib_map(VariableMap());
337 fragment_shader_->set_uniform_map(VariableMap());
338 fragment_shader_->set_varying_map(VariableMap());
340 SetExpectationsForLoadLinkedProgram(kProgramId, &emulator);
343 cache_->LoadProgram(shader_cache_shader());
345 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram(
352 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
353 base::Unretained(this))));
355 // apparently the hash_map implementation on android doesn't have the
357 #if !defined(OS_ANDROID)
358 EXPECT_EQ(vertex_attrib_map, vertex_shader_->attrib_map());
359 EXPECT_EQ(vertex_uniform_map, vertex_shader_->uniform_map());
360 EXPECT_EQ(vertex_varying_map, vertex_shader_->varying_map());
361 EXPECT_EQ(fragment_attrib_map, fragment_shader_->attrib_map());
362 EXPECT_EQ(fragment_uniform_map, fragment_shader_->uniform_map());
363 EXPECT_EQ(fragment_varying_map, fragment_shader_->varying_map());
367 TEST_F(MemoryProgramCacheTest, LoadFailOnLinkFalse) {
368 const GLenum kFormat = 1;
369 const int kProgramId = 10;
370 const int kBinaryLength = 20;
371 char test_binary[kBinaryLength];
372 for (int i = 0; i < kBinaryLength; ++i) {
375 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
377 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
378 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
379 fragment_shader_, NULL, NULL,
380 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
381 base::Unretained(this)));
383 SetExpectationsForLoadLinkedProgramFailure(kProgramId, &emulator);
384 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
391 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
392 base::Unretained(this))));
395 TEST_F(MemoryProgramCacheTest, LoadFailOnDifferentSource) {
396 const GLenum kFormat = 1;
397 const int kProgramId = 10;
398 const int kBinaryLength = 20;
399 char test_binary[kBinaryLength];
400 for (int i = 0; i < kBinaryLength; ++i) {
403 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
405 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
406 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
407 fragment_shader_, NULL, NULL,
408 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
409 base::Unretained(this)));
411 const std::string vertex_orig_source =
412 *vertex_shader_->signature_source();
413 vertex_shader_->UpdateSource("different!");
414 vertex_shader_->SetStatus(true, NULL, NULL);
415 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
422 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
423 base::Unretained(this))));
425 vertex_shader_->UpdateSource(vertex_orig_source.c_str());
426 vertex_shader_->SetStatus(true, NULL, NULL);
427 fragment_shader_->UpdateSource("different!");
428 fragment_shader_->SetStatus(true, NULL, NULL);
429 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
436 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
437 base::Unretained(this))));
440 TEST_F(MemoryProgramCacheTest, LoadFailOnDifferentMap) {
441 const GLenum kFormat = 1;
442 const int kProgramId = 10;
443 const int kBinaryLength = 20;
444 char test_binary[kBinaryLength];
445 for (int i = 0; i < kBinaryLength; ++i) {
448 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
450 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
451 ProgramCache::LocationMap binding_map;
452 binding_map["test"] = 512;
453 cache_->SaveLinkedProgram(kProgramId,
459 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
460 base::Unretained(this)));
462 binding_map["different!"] = 59;
463 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
470 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
471 base::Unretained(this))));
472 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
479 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
480 base::Unretained(this))));
483 TEST_F(MemoryProgramCacheTest, MemoryProgramCacheEviction) {
484 typedef ShaderTranslator::VariableMap VariableMap;
485 const GLenum kFormat = 1;
486 const int kProgramId = 10;
487 const int kBinaryLength = 20;
488 char test_binary[kBinaryLength];
489 for (int i = 0; i < kBinaryLength; ++i) {
492 ProgramBinaryEmulator emulator1(kBinaryLength, kFormat, test_binary);
495 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator1);
496 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
497 fragment_shader_, NULL, NULL,
498 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
499 base::Unretained(this)));
501 const int kEvictingProgramId = 11;
502 const GLuint kEvictingBinaryLength = kCacheSizeBytes - kBinaryLength + 1;
504 // save old source and modify for new program
505 const std::string old_source =
506 *fragment_shader_->signature_source();
507 fragment_shader_->UpdateSource("al sdfkjdk");
508 fragment_shader_->SetStatus(true, NULL, NULL);
510 scoped_ptr<char[]> bigTestBinary =
511 scoped_ptr<char[]>(new char[kEvictingBinaryLength]);
512 for (size_t i = 0; i < kEvictingBinaryLength; ++i) {
513 bigTestBinary[i] = i % 250;
515 ProgramBinaryEmulator emulator2(kEvictingBinaryLength,
517 bigTestBinary.get());
519 SetExpectationsForSaveLinkedProgram(kEvictingProgramId, &emulator2);
520 cache_->SaveLinkedProgram(kEvictingProgramId,
526 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
527 base::Unretained(this)));
529 EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
530 *vertex_shader_->signature_source(),
532 *fragment_shader_->signature_source(),
535 EXPECT_EQ(ProgramCache::LINK_UNKNOWN, cache_->GetLinkedProgramStatus(
538 *fragment_shader_->signature_source(),
543 TEST_F(MemoryProgramCacheTest, SaveCorrectProgram) {
544 const GLenum kFormat = 1;
545 const int kProgramId = 10;
546 const int kBinaryLength = 20;
547 char test_binary[kBinaryLength];
548 for (int i = 0; i < kBinaryLength; ++i) {
551 ProgramBinaryEmulator emulator1(kBinaryLength, kFormat, test_binary);
553 vertex_shader_->UpdateSource("different!");
554 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator1);
555 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
556 fragment_shader_, NULL, NULL,
557 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
558 base::Unretained(this)));
560 EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
561 *vertex_shader_->signature_source(),
563 *fragment_shader_->signature_source(),
568 TEST_F(MemoryProgramCacheTest, LoadCorrectProgram) {
569 const GLenum kFormat = 1;
570 const int kProgramId = 10;
571 const int kBinaryLength = 20;
572 char test_binary[kBinaryLength];
573 for (int i = 0; i < kBinaryLength; ++i) {
576 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
578 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
579 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
580 fragment_shader_, NULL, NULL,
581 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
582 base::Unretained(this)));
584 EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
585 *vertex_shader_->signature_source(),
587 *fragment_shader_->signature_source(),
591 SetExpectationsForLoadLinkedProgram(kProgramId, &emulator);
593 fragment_shader_->UpdateSource("different!");
594 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram(
601 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
602 base::Unretained(this))));
605 TEST_F(MemoryProgramCacheTest, OverwriteOnNewSave) {
606 const GLenum kFormat = 1;
607 const int kProgramId = 10;
608 const int kBinaryLength = 20;
609 char test_binary[kBinaryLength];
610 for (int i = 0; i < kBinaryLength; ++i) {
613 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
615 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
616 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
617 fragment_shader_, NULL, NULL,
618 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
619 base::Unretained(this)));
622 char test_binary2[kBinaryLength];
623 for (int i = 0; i < kBinaryLength; ++i) {
624 test_binary2[i] = (i*2) % 250;
626 ProgramBinaryEmulator emulator2(kBinaryLength, kFormat, test_binary2);
627 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator2);
628 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
629 fragment_shader_, NULL, NULL,
630 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
631 base::Unretained(this)));
633 SetExpectationsForLoadLinkedProgram(kProgramId, &emulator2);
634 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram(
641 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
642 base::Unretained(this))));