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/gpu_service_test.h"
11 #include "gpu/command_buffer/service/shader_manager.h"
12 #include "gpu/command_buffer/service/shader_translator.h"
13 #include "gpu/command_buffer/service/test_helper.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "ui/gl/gl_bindings.h"
16 #include "ui/gl/gl_mock.h"
19 using ::testing::ElementsAreArray;
20 using ::testing::Invoke;
21 using ::testing::SetArgPointee;
22 using ::testing::SetArrayArgument;
27 class ProgramBinaryEmulator {
29 ProgramBinaryEmulator(GLsizei length,
36 void GetProgramBinary(GLuint program,
45 memcpy(binary, binary_, length_);
48 void ProgramBinary(GLuint program,
52 // format and length are verified by matcher
53 EXPECT_EQ(0, memcmp(binary_, binary, length));
56 GLsizei length() const { return length_; }
57 GLenum format() const { return format_; }
58 const char* binary() const { return binary_; }
66 class MemoryProgramCacheTest : public GpuServiceTest {
68 static const size_t kCacheSizeBytes = 1024;
69 static const GLuint kVertexShaderClientId = 90;
70 static const GLuint kVertexShaderServiceId = 100;
71 static const GLuint kFragmentShaderClientId = 91;
72 static const GLuint kFragmentShaderServiceId = 100;
74 MemoryProgramCacheTest()
75 : cache_(new MemoryProgramCache(kCacheSizeBytes)),
77 fragment_shader_(NULL),
78 shader_cache_count_(0) { }
79 ~MemoryProgramCacheTest() override { shader_manager_.Destroy(false); }
81 void ShaderCacheCb(const std::string& key, const std::string& shader) {
82 shader_cache_count_++;
83 shader_cache_shader_ = shader;
86 int32 shader_cache_count() { return shader_cache_count_; }
87 const std::string& shader_cache_shader() { return shader_cache_shader_; }
90 void SetUp() override {
91 GpuServiceTest::SetUp();
93 vertex_shader_ = shader_manager_.CreateShader(kVertexShaderClientId,
94 kVertexShaderServiceId,
96 fragment_shader_ = shader_manager_.CreateShader(
97 kFragmentShaderClientId,
98 kFragmentShaderServiceId,
100 ASSERT_TRUE(vertex_shader_ != NULL);
101 ASSERT_TRUE(fragment_shader_ != NULL);
102 AttributeMap vertex_attrib_map;
103 UniformMap vertex_uniform_map;
104 VaryingMap vertex_varying_map;
105 AttributeMap fragment_attrib_map;
106 UniformMap fragment_uniform_map;
107 VaryingMap fragment_varying_map;
109 vertex_attrib_map["a"] = TestHelper::ConstructAttribute(
110 GL_FLOAT_VEC2, 34, GL_LOW_FLOAT, false, "a");
111 vertex_uniform_map["a"] = TestHelper::ConstructUniform(
112 GL_FLOAT, 10, GL_MEDIUM_FLOAT, true, "a");
113 vertex_uniform_map["b"] = TestHelper::ConstructUniform(
114 GL_FLOAT_VEC3, 3114, GL_HIGH_FLOAT, true, "b");
115 vertex_varying_map["c"] = TestHelper::ConstructVarying(
116 GL_FLOAT_VEC4, 2, GL_HIGH_FLOAT, true, "c");
117 fragment_attrib_map["jjjbb"] = TestHelper::ConstructAttribute(
118 GL_FLOAT_MAT4, 1114, GL_MEDIUM_FLOAT, false, "jjjbb");
119 fragment_uniform_map["k"] = TestHelper::ConstructUniform(
120 GL_FLOAT_MAT2, 34413, GL_MEDIUM_FLOAT, true, "k");
121 fragment_varying_map["c"] = TestHelper::ConstructVarying(
122 GL_FLOAT_VEC4, 2, GL_HIGH_FLOAT, true, "c");
124 vertex_shader_->set_source("bbbalsldkdkdkd");
125 fragment_shader_->set_source("bbbal sldkdkdkas 134 ad");
127 TestHelper::SetShaderStates(
128 gl_.get(), vertex_shader_, true, NULL, NULL,
129 &vertex_attrib_map, &vertex_uniform_map, &vertex_varying_map,
131 TestHelper::SetShaderStates(
132 gl_.get(), fragment_shader_, true, NULL, NULL,
133 &fragment_attrib_map, &fragment_uniform_map, &fragment_varying_map,
137 void SetExpectationsForSaveLinkedProgram(
138 const GLint program_id,
139 ProgramBinaryEmulator* emulator) const {
140 EXPECT_CALL(*gl_.get(),
141 GetProgramiv(program_id, GL_PROGRAM_BINARY_LENGTH_OES, _))
142 .WillOnce(SetArgPointee<2>(emulator->length()));
143 EXPECT_CALL(*gl_.get(),
144 GetProgramBinary(program_id, emulator->length(), _, _, _))
145 .WillOnce(Invoke(emulator, &ProgramBinaryEmulator::GetProgramBinary));
148 void SetExpectationsForLoadLinkedProgram(
149 const GLint program_id,
150 ProgramBinaryEmulator* emulator) const {
151 EXPECT_CALL(*gl_.get(),
152 ProgramBinary(program_id,
156 .WillOnce(Invoke(emulator, &ProgramBinaryEmulator::ProgramBinary));
157 EXPECT_CALL(*gl_.get(),
158 GetProgramiv(program_id, GL_LINK_STATUS, _))
159 .WillOnce(SetArgPointee<2>(GL_TRUE));
162 void SetExpectationsForLoadLinkedProgramFailure(
163 const GLint program_id,
164 ProgramBinaryEmulator* emulator) const {
165 EXPECT_CALL(*gl_.get(),
166 ProgramBinary(program_id,
170 .WillOnce(Invoke(emulator, &ProgramBinaryEmulator::ProgramBinary));
171 EXPECT_CALL(*gl_.get(),
172 GetProgramiv(program_id, GL_LINK_STATUS, _))
173 .WillOnce(SetArgPointee<2>(GL_FALSE));
176 scoped_ptr<MemoryProgramCache> cache_;
177 ShaderManager shader_manager_;
178 Shader* vertex_shader_;
179 Shader* fragment_shader_;
180 int32 shader_cache_count_;
181 std::string shader_cache_shader_;
184 TEST_F(MemoryProgramCacheTest, CacheSave) {
185 const GLenum kFormat = 1;
186 const int kProgramId = 10;
187 const int kBinaryLength = 20;
188 char test_binary[kBinaryLength];
189 for (int i = 0; i < kBinaryLength; ++i) {
192 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
194 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
195 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
196 fragment_shader_, NULL, NULL,
197 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
198 base::Unretained(this)));
200 EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
201 vertex_shader_->signature_source(),
203 fragment_shader_->signature_source(),
206 EXPECT_EQ(1, shader_cache_count());
209 TEST_F(MemoryProgramCacheTest, LoadProgram) {
210 const GLenum kFormat = 1;
211 const int kProgramId = 10;
212 const int kBinaryLength = 20;
213 char test_binary[kBinaryLength];
214 for (int i = 0; i < kBinaryLength; ++i) {
217 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
219 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
220 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
221 fragment_shader_, NULL, NULL,
222 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
223 base::Unretained(this)));
225 EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
226 vertex_shader_->signature_source(),
228 fragment_shader_->signature_source(),
231 EXPECT_EQ(1, shader_cache_count());
235 cache_->LoadProgram(shader_cache_shader());
236 EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
237 vertex_shader_->signature_source(),
239 fragment_shader_->signature_source(),
244 TEST_F(MemoryProgramCacheTest, CacheLoadMatchesSave) {
245 const GLenum kFormat = 1;
246 const int kProgramId = 10;
247 const int kBinaryLength = 20;
248 char test_binary[kBinaryLength];
249 for (int i = 0; i < kBinaryLength; ++i) {
252 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
254 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
255 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
256 fragment_shader_, NULL, NULL,
257 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
258 base::Unretained(this)));
259 EXPECT_EQ(1, shader_cache_count());
261 AttributeMap vertex_attrib_map = vertex_shader_->attrib_map();
262 UniformMap vertex_uniform_map = vertex_shader_->uniform_map();
263 VaryingMap vertex_varying_map = vertex_shader_->varying_map();
264 AttributeMap fragment_attrib_map = fragment_shader_->attrib_map();
265 UniformMap fragment_uniform_map = fragment_shader_->uniform_map();
266 VaryingMap fragment_varying_map = fragment_shader_->varying_map();
268 vertex_shader_->set_attrib_map(AttributeMap());
269 vertex_shader_->set_uniform_map(UniformMap());
270 vertex_shader_->set_varying_map(VaryingMap());
271 fragment_shader_->set_attrib_map(AttributeMap());
272 fragment_shader_->set_uniform_map(UniformMap());
273 fragment_shader_->set_varying_map(VaryingMap());
275 SetExpectationsForLoadLinkedProgram(kProgramId, &emulator);
277 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram(
284 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
285 base::Unretained(this))));
287 // apparently the hash_map implementation on android doesn't have the
289 #if !defined(OS_ANDROID)
290 EXPECT_EQ(vertex_attrib_map, vertex_shader_->attrib_map());
291 EXPECT_EQ(vertex_uniform_map, vertex_shader_->uniform_map());
292 EXPECT_EQ(vertex_varying_map, vertex_shader_->varying_map());
293 EXPECT_EQ(fragment_attrib_map, fragment_shader_->attrib_map());
294 EXPECT_EQ(fragment_uniform_map, fragment_shader_->uniform_map());
295 EXPECT_EQ(fragment_varying_map, fragment_shader_->varying_map());
299 TEST_F(MemoryProgramCacheTest, LoadProgramMatchesSave) {
300 const GLenum kFormat = 1;
301 const int kProgramId = 10;
302 const int kBinaryLength = 20;
303 char test_binary[kBinaryLength];
304 for (int i = 0; i < kBinaryLength; ++i) {
307 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
309 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
310 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
311 fragment_shader_, NULL, NULL,
312 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
313 base::Unretained(this)));
314 EXPECT_EQ(1, shader_cache_count());
316 AttributeMap vertex_attrib_map = vertex_shader_->attrib_map();
317 UniformMap vertex_uniform_map = vertex_shader_->uniform_map();
318 VaryingMap vertex_varying_map = vertex_shader_->varying_map();
319 AttributeMap fragment_attrib_map = fragment_shader_->attrib_map();
320 UniformMap fragment_uniform_map = fragment_shader_->uniform_map();
321 VaryingMap fragment_varying_map = fragment_shader_->varying_map();
323 vertex_shader_->set_attrib_map(AttributeMap());
324 vertex_shader_->set_uniform_map(UniformMap());
325 vertex_shader_->set_varying_map(VaryingMap());
326 fragment_shader_->set_attrib_map(AttributeMap());
327 fragment_shader_->set_uniform_map(UniformMap());
328 fragment_shader_->set_varying_map(VaryingMap());
330 SetExpectationsForLoadLinkedProgram(kProgramId, &emulator);
333 cache_->LoadProgram(shader_cache_shader());
335 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram(
342 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
343 base::Unretained(this))));
345 // apparently the hash_map implementation on android doesn't have the
347 #if !defined(OS_ANDROID)
348 EXPECT_EQ(vertex_attrib_map, vertex_shader_->attrib_map());
349 EXPECT_EQ(vertex_uniform_map, vertex_shader_->uniform_map());
350 EXPECT_EQ(vertex_varying_map, vertex_shader_->varying_map());
351 EXPECT_EQ(fragment_attrib_map, fragment_shader_->attrib_map());
352 EXPECT_EQ(fragment_uniform_map, fragment_shader_->uniform_map());
353 EXPECT_EQ(fragment_varying_map, fragment_shader_->varying_map());
357 TEST_F(MemoryProgramCacheTest, LoadFailOnLinkFalse) {
358 const GLenum kFormat = 1;
359 const int kProgramId = 10;
360 const int kBinaryLength = 20;
361 char test_binary[kBinaryLength];
362 for (int i = 0; i < kBinaryLength; ++i) {
365 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
367 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
368 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
369 fragment_shader_, NULL, NULL,
370 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
371 base::Unretained(this)));
373 SetExpectationsForLoadLinkedProgramFailure(kProgramId, &emulator);
374 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
381 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
382 base::Unretained(this))));
385 TEST_F(MemoryProgramCacheTest, LoadFailOnDifferentSource) {
386 const GLenum kFormat = 1;
387 const int kProgramId = 10;
388 const int kBinaryLength = 20;
389 char test_binary[kBinaryLength];
390 for (int i = 0; i < kBinaryLength; ++i) {
393 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
395 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
396 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
397 fragment_shader_, NULL, NULL,
398 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
399 base::Unretained(this)));
401 const std::string vertex_orig_source = vertex_shader_->signature_source();
402 vertex_shader_->set_source("different!");
403 TestHelper::SetShaderStates(gl_.get(), vertex_shader_, true);
404 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
411 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
412 base::Unretained(this))));
414 vertex_shader_->set_source(vertex_orig_source);
415 TestHelper::SetShaderStates(gl_.get(), vertex_shader_, true);
416 fragment_shader_->set_source("different!");
417 TestHelper::SetShaderStates(gl_.get(), fragment_shader_, true);
418 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
425 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
426 base::Unretained(this))));
429 TEST_F(MemoryProgramCacheTest, LoadFailOnDifferentMap) {
430 const GLenum kFormat = 1;
431 const int kProgramId = 10;
432 const int kBinaryLength = 20;
433 char test_binary[kBinaryLength];
434 for (int i = 0; i < kBinaryLength; ++i) {
437 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
439 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
440 ProgramCache::LocationMap binding_map;
441 binding_map["test"] = 512;
442 cache_->SaveLinkedProgram(kProgramId,
448 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
449 base::Unretained(this)));
451 binding_map["different!"] = 59;
452 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
459 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
460 base::Unretained(this))));
461 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
468 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
469 base::Unretained(this))));
472 TEST_F(MemoryProgramCacheTest, MemoryProgramCacheEviction) {
473 const GLenum kFormat = 1;
474 const int kProgramId = 10;
475 const int kBinaryLength = 20;
476 char test_binary[kBinaryLength];
477 for (int i = 0; i < kBinaryLength; ++i) {
480 ProgramBinaryEmulator emulator1(kBinaryLength, kFormat, test_binary);
483 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator1);
484 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
485 fragment_shader_, NULL, NULL,
486 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
487 base::Unretained(this)));
489 const int kEvictingProgramId = 11;
490 const GLuint kEvictingBinaryLength = kCacheSizeBytes - kBinaryLength + 1;
492 // save old source and modify for new program
493 const std::string& old_source = fragment_shader_->signature_source();
494 fragment_shader_->set_source("al sdfkjdk");
495 TestHelper::SetShaderStates(gl_.get(), fragment_shader_, true);
497 scoped_ptr<char[]> bigTestBinary =
498 scoped_ptr<char[]>(new char[kEvictingBinaryLength]);
499 for (size_t i = 0; i < kEvictingBinaryLength; ++i) {
500 bigTestBinary[i] = i % 250;
502 ProgramBinaryEmulator emulator2(kEvictingBinaryLength,
504 bigTestBinary.get());
506 SetExpectationsForSaveLinkedProgram(kEvictingProgramId, &emulator2);
507 cache_->SaveLinkedProgram(kEvictingProgramId,
513 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
514 base::Unretained(this)));
516 EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
517 vertex_shader_->signature_source(),
519 fragment_shader_->signature_source(),
522 EXPECT_EQ(ProgramCache::LINK_UNKNOWN, cache_->GetLinkedProgramStatus(
525 fragment_shader_->signature_source(),
530 TEST_F(MemoryProgramCacheTest, SaveCorrectProgram) {
531 const GLenum kFormat = 1;
532 const int kProgramId = 10;
533 const int kBinaryLength = 20;
534 char test_binary[kBinaryLength];
535 for (int i = 0; i < kBinaryLength; ++i) {
538 ProgramBinaryEmulator emulator1(kBinaryLength, kFormat, test_binary);
540 vertex_shader_->set_source("different!");
541 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator1);
542 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
543 fragment_shader_, NULL, NULL,
544 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
545 base::Unretained(this)));
547 EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
548 vertex_shader_->signature_source(),
550 fragment_shader_->signature_source(),
555 TEST_F(MemoryProgramCacheTest, LoadCorrectProgram) {
556 const GLenum kFormat = 1;
557 const int kProgramId = 10;
558 const int kBinaryLength = 20;
559 char test_binary[kBinaryLength];
560 for (int i = 0; i < kBinaryLength; ++i) {
563 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
565 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
566 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
567 fragment_shader_, NULL, NULL,
568 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
569 base::Unretained(this)));
571 EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
572 vertex_shader_->signature_source(),
574 fragment_shader_->signature_source(),
578 SetExpectationsForLoadLinkedProgram(kProgramId, &emulator);
580 fragment_shader_->set_source("different!");
581 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram(
588 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
589 base::Unretained(this))));
592 TEST_F(MemoryProgramCacheTest, OverwriteOnNewSave) {
593 const GLenum kFormat = 1;
594 const int kProgramId = 10;
595 const int kBinaryLength = 20;
596 char test_binary[kBinaryLength];
597 for (int i = 0; i < kBinaryLength; ++i) {
600 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
602 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
603 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
604 fragment_shader_, NULL, NULL,
605 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
606 base::Unretained(this)));
609 char test_binary2[kBinaryLength];
610 for (int i = 0; i < kBinaryLength; ++i) {
611 test_binary2[i] = (i*2) % 250;
613 ProgramBinaryEmulator emulator2(kBinaryLength, kFormat, test_binary2);
614 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator2);
615 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
616 fragment_shader_, NULL, NULL,
617 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
618 base::Unretained(this)));
620 SetExpectationsForLoadLinkedProgram(kProgramId, &emulator2);
621 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram(
628 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
629 base::Unretained(this))));