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;
25 typedef gpu::gles2::ShaderTranslator::VariableMap VariableMap;
26 } // anonymous namespace
31 class ProgramBinaryEmulator {
33 ProgramBinaryEmulator(GLsizei length,
40 void GetProgramBinary(GLuint program,
49 memcpy(binary, binary_, length_);
52 void ProgramBinary(GLuint program,
56 // format and length are verified by matcher
57 EXPECT_EQ(0, memcmp(binary_, binary, length));
60 GLsizei length() const { return length_; }
61 GLenum format() const { return format_; }
62 const char* binary() const { return binary_; }
70 class MemoryProgramCacheTest : public GpuServiceTest {
72 static const size_t kCacheSizeBytes = 1024;
73 static const GLuint kVertexShaderClientId = 90;
74 static const GLuint kVertexShaderServiceId = 100;
75 static const GLuint kFragmentShaderClientId = 91;
76 static const GLuint kFragmentShaderServiceId = 100;
78 MemoryProgramCacheTest()
79 : cache_(new MemoryProgramCache(kCacheSizeBytes)),
81 fragment_shader_(NULL),
82 shader_cache_count_(0) { }
83 virtual ~MemoryProgramCacheTest() {
84 shader_manager_.Destroy(false);
87 void ShaderCacheCb(const std::string& key, const std::string& shader) {
88 shader_cache_count_++;
89 shader_cache_shader_ = shader;
92 int32 shader_cache_count() { return shader_cache_count_; }
93 const std::string& shader_cache_shader() { return shader_cache_shader_; }
96 virtual void SetUp() {
97 GpuServiceTest::SetUp();
99 vertex_shader_ = shader_manager_.CreateShader(kVertexShaderClientId,
100 kVertexShaderServiceId,
102 fragment_shader_ = shader_manager_.CreateShader(
103 kFragmentShaderClientId,
104 kFragmentShaderServiceId,
106 ASSERT_TRUE(vertex_shader_ != NULL);
107 ASSERT_TRUE(fragment_shader_ != NULL);
108 typedef ShaderTranslatorInterface::VariableInfo VariableInfo;
109 typedef ShaderTranslator::VariableMap VariableMap;
110 VariableMap vertex_attrib_map;
111 VariableMap vertex_uniform_map;
112 VariableMap vertex_varying_map;
113 VariableMap fragment_attrib_map;
114 VariableMap fragment_uniform_map;
115 VariableMap fragment_varying_map;
117 vertex_attrib_map["a"] = VariableInfo(1, 34, SH_PRECISION_LOWP, 0, "a");
118 vertex_uniform_map["a"] = VariableInfo(0, 10, SH_PRECISION_MEDIUMP, 1, "a");
119 vertex_uniform_map["b"] = VariableInfo(2, 3114, SH_PRECISION_HIGHP, 1, "b");
120 vertex_varying_map["c"] = VariableInfo(3, 2, SH_PRECISION_HIGHP, 1, "c");
121 fragment_attrib_map["jjjbb"] =
122 VariableInfo(463, 1114, SH_PRECISION_MEDIUMP, 0, "jjjbb");
123 fragment_uniform_map["k"] =
124 VariableInfo(10, 34413, SH_PRECISION_MEDIUMP, 1, "k");
125 fragment_varying_map["c"] = VariableInfo(3, 2, SH_PRECISION_HIGHP, 1, "c");
127 vertex_shader_->set_source("bbbalsldkdkdkd");
128 fragment_shader_->set_source("bbbal sldkdkdkas 134 ad");
130 TestHelper::SetShaderStates(
131 gl_.get(), vertex_shader_, true, NULL, NULL,
132 &vertex_attrib_map, &vertex_uniform_map, &vertex_varying_map,
134 TestHelper::SetShaderStates(
135 gl_.get(), fragment_shader_, true, NULL, NULL,
136 &fragment_attrib_map, &fragment_uniform_map, &fragment_varying_map,
140 void SetExpectationsForSaveLinkedProgram(
141 const GLint program_id,
142 ProgramBinaryEmulator* emulator) const {
143 EXPECT_CALL(*gl_.get(),
144 GetProgramiv(program_id, GL_PROGRAM_BINARY_LENGTH_OES, _))
145 .WillOnce(SetArgPointee<2>(emulator->length()));
146 EXPECT_CALL(*gl_.get(),
147 GetProgramBinary(program_id, emulator->length(), _, _, _))
148 .WillOnce(Invoke(emulator, &ProgramBinaryEmulator::GetProgramBinary));
151 void SetExpectationsForLoadLinkedProgram(
152 const GLint program_id,
153 ProgramBinaryEmulator* emulator) const {
154 EXPECT_CALL(*gl_.get(),
155 ProgramBinary(program_id,
159 .WillOnce(Invoke(emulator, &ProgramBinaryEmulator::ProgramBinary));
160 EXPECT_CALL(*gl_.get(),
161 GetProgramiv(program_id, GL_LINK_STATUS, _))
162 .WillOnce(SetArgPointee<2>(GL_TRUE));
165 void SetExpectationsForLoadLinkedProgramFailure(
166 const GLint program_id,
167 ProgramBinaryEmulator* emulator) const {
168 EXPECT_CALL(*gl_.get(),
169 ProgramBinary(program_id,
173 .WillOnce(Invoke(emulator, &ProgramBinaryEmulator::ProgramBinary));
174 EXPECT_CALL(*gl_.get(),
175 GetProgramiv(program_id, GL_LINK_STATUS, _))
176 .WillOnce(SetArgPointee<2>(GL_FALSE));
179 scoped_ptr<MemoryProgramCache> cache_;
180 ShaderManager shader_manager_;
181 Shader* vertex_shader_;
182 Shader* fragment_shader_;
183 int32 shader_cache_count_;
184 std::string shader_cache_shader_;
187 TEST_F(MemoryProgramCacheTest, CacheSave) {
188 const GLenum kFormat = 1;
189 const int kProgramId = 10;
190 const int kBinaryLength = 20;
191 char test_binary[kBinaryLength];
192 for (int i = 0; i < kBinaryLength; ++i) {
195 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
197 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
198 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
199 fragment_shader_, NULL, NULL,
200 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
201 base::Unretained(this)));
203 EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
204 vertex_shader_->signature_source(),
206 fragment_shader_->signature_source(),
209 EXPECT_EQ(1, shader_cache_count());
212 TEST_F(MemoryProgramCacheTest, LoadProgram) {
213 const GLenum kFormat = 1;
214 const int kProgramId = 10;
215 const int kBinaryLength = 20;
216 char test_binary[kBinaryLength];
217 for (int i = 0; i < kBinaryLength; ++i) {
220 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
222 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
223 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
224 fragment_shader_, NULL, NULL,
225 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
226 base::Unretained(this)));
228 EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
229 vertex_shader_->signature_source(),
231 fragment_shader_->signature_source(),
234 EXPECT_EQ(1, shader_cache_count());
238 cache_->LoadProgram(shader_cache_shader());
239 EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
240 vertex_shader_->signature_source(),
242 fragment_shader_->signature_source(),
247 TEST_F(MemoryProgramCacheTest, CacheLoadMatchesSave) {
248 const GLenum kFormat = 1;
249 const int kProgramId = 10;
250 const int kBinaryLength = 20;
251 char test_binary[kBinaryLength];
252 for (int i = 0; i < kBinaryLength; ++i) {
255 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
257 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
258 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
259 fragment_shader_, NULL, NULL,
260 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
261 base::Unretained(this)));
262 EXPECT_EQ(1, shader_cache_count());
264 VariableMap vertex_attrib_map = vertex_shader_->attrib_map();
265 VariableMap vertex_uniform_map = vertex_shader_->uniform_map();
266 VariableMap vertex_varying_map = vertex_shader_->varying_map();
267 VariableMap fragment_attrib_map = fragment_shader_->attrib_map();
268 VariableMap fragment_uniform_map = fragment_shader_->uniform_map();
269 VariableMap fragment_varying_map = fragment_shader_->varying_map();
271 vertex_shader_->set_attrib_map(VariableMap());
272 vertex_shader_->set_uniform_map(VariableMap());
273 vertex_shader_->set_varying_map(VariableMap());
274 fragment_shader_->set_attrib_map(VariableMap());
275 fragment_shader_->set_uniform_map(VariableMap());
276 fragment_shader_->set_varying_map(VariableMap());
278 SetExpectationsForLoadLinkedProgram(kProgramId, &emulator);
280 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram(
287 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
288 base::Unretained(this))));
290 // apparently the hash_map implementation on android doesn't have the
292 #if !defined(OS_ANDROID)
293 EXPECT_EQ(vertex_attrib_map, vertex_shader_->attrib_map());
294 EXPECT_EQ(vertex_uniform_map, vertex_shader_->uniform_map());
295 EXPECT_EQ(vertex_varying_map, vertex_shader_->varying_map());
296 EXPECT_EQ(fragment_attrib_map, fragment_shader_->attrib_map());
297 EXPECT_EQ(fragment_uniform_map, fragment_shader_->uniform_map());
298 EXPECT_EQ(fragment_varying_map, fragment_shader_->varying_map());
302 TEST_F(MemoryProgramCacheTest, LoadProgramMatchesSave) {
303 const GLenum kFormat = 1;
304 const int kProgramId = 10;
305 const int kBinaryLength = 20;
306 char test_binary[kBinaryLength];
307 for (int i = 0; i < kBinaryLength; ++i) {
310 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
312 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
313 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
314 fragment_shader_, NULL, NULL,
315 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
316 base::Unretained(this)));
317 EXPECT_EQ(1, shader_cache_count());
319 VariableMap vertex_attrib_map = vertex_shader_->attrib_map();
320 VariableMap vertex_uniform_map = vertex_shader_->uniform_map();
321 VariableMap vertex_varying_map = vertex_shader_->varying_map();
322 VariableMap fragment_attrib_map = fragment_shader_->attrib_map();
323 VariableMap fragment_uniform_map = fragment_shader_->uniform_map();
324 VariableMap fragment_varying_map = fragment_shader_->varying_map();
326 vertex_shader_->set_attrib_map(VariableMap());
327 vertex_shader_->set_uniform_map(VariableMap());
328 vertex_shader_->set_varying_map(VariableMap());
329 fragment_shader_->set_attrib_map(VariableMap());
330 fragment_shader_->set_uniform_map(VariableMap());
331 fragment_shader_->set_varying_map(VariableMap());
333 SetExpectationsForLoadLinkedProgram(kProgramId, &emulator);
336 cache_->LoadProgram(shader_cache_shader());
338 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram(
345 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
346 base::Unretained(this))));
348 // apparently the hash_map implementation on android doesn't have the
350 #if !defined(OS_ANDROID)
351 EXPECT_EQ(vertex_attrib_map, vertex_shader_->attrib_map());
352 EXPECT_EQ(vertex_uniform_map, vertex_shader_->uniform_map());
353 EXPECT_EQ(vertex_varying_map, vertex_shader_->varying_map());
354 EXPECT_EQ(fragment_attrib_map, fragment_shader_->attrib_map());
355 EXPECT_EQ(fragment_uniform_map, fragment_shader_->uniform_map());
356 EXPECT_EQ(fragment_varying_map, fragment_shader_->varying_map());
360 TEST_F(MemoryProgramCacheTest, LoadFailOnLinkFalse) {
361 const GLenum kFormat = 1;
362 const int kProgramId = 10;
363 const int kBinaryLength = 20;
364 char test_binary[kBinaryLength];
365 for (int i = 0; i < kBinaryLength; ++i) {
368 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
370 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
371 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
372 fragment_shader_, NULL, NULL,
373 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
374 base::Unretained(this)));
376 SetExpectationsForLoadLinkedProgramFailure(kProgramId, &emulator);
377 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
384 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
385 base::Unretained(this))));
388 TEST_F(MemoryProgramCacheTest, LoadFailOnDifferentSource) {
389 const GLenum kFormat = 1;
390 const int kProgramId = 10;
391 const int kBinaryLength = 20;
392 char test_binary[kBinaryLength];
393 for (int i = 0; i < kBinaryLength; ++i) {
396 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
398 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
399 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
400 fragment_shader_, NULL, NULL,
401 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
402 base::Unretained(this)));
404 const std::string vertex_orig_source = vertex_shader_->signature_source();
405 vertex_shader_->set_source("different!");
406 TestHelper::SetShaderStates(gl_.get(), vertex_shader_, true);
407 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
414 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
415 base::Unretained(this))));
417 vertex_shader_->set_source(vertex_orig_source);
418 TestHelper::SetShaderStates(gl_.get(), vertex_shader_, true);
419 fragment_shader_->set_source("different!");
420 TestHelper::SetShaderStates(gl_.get(), fragment_shader_, true);
421 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
428 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
429 base::Unretained(this))));
432 TEST_F(MemoryProgramCacheTest, LoadFailOnDifferentMap) {
433 const GLenum kFormat = 1;
434 const int kProgramId = 10;
435 const int kBinaryLength = 20;
436 char test_binary[kBinaryLength];
437 for (int i = 0; i < kBinaryLength; ++i) {
440 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
442 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
443 ProgramCache::LocationMap binding_map;
444 binding_map["test"] = 512;
445 cache_->SaveLinkedProgram(kProgramId,
451 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
452 base::Unretained(this)));
454 binding_map["different!"] = 59;
455 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
462 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
463 base::Unretained(this))));
464 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
471 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
472 base::Unretained(this))));
475 TEST_F(MemoryProgramCacheTest, MemoryProgramCacheEviction) {
476 const GLenum kFormat = 1;
477 const int kProgramId = 10;
478 const int kBinaryLength = 20;
479 char test_binary[kBinaryLength];
480 for (int i = 0; i < kBinaryLength; ++i) {
483 ProgramBinaryEmulator emulator1(kBinaryLength, kFormat, test_binary);
486 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator1);
487 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
488 fragment_shader_, NULL, NULL,
489 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
490 base::Unretained(this)));
492 const int kEvictingProgramId = 11;
493 const GLuint kEvictingBinaryLength = kCacheSizeBytes - kBinaryLength + 1;
495 // save old source and modify for new program
496 const std::string& old_source = fragment_shader_->signature_source();
497 fragment_shader_->set_source("al sdfkjdk");
498 TestHelper::SetShaderStates(gl_.get(), fragment_shader_, true);
500 scoped_ptr<char[]> bigTestBinary =
501 scoped_ptr<char[]>(new char[kEvictingBinaryLength]);
502 for (size_t i = 0; i < kEvictingBinaryLength; ++i) {
503 bigTestBinary[i] = i % 250;
505 ProgramBinaryEmulator emulator2(kEvictingBinaryLength,
507 bigTestBinary.get());
509 SetExpectationsForSaveLinkedProgram(kEvictingProgramId, &emulator2);
510 cache_->SaveLinkedProgram(kEvictingProgramId,
516 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
517 base::Unretained(this)));
519 EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
520 vertex_shader_->signature_source(),
522 fragment_shader_->signature_source(),
525 EXPECT_EQ(ProgramCache::LINK_UNKNOWN, cache_->GetLinkedProgramStatus(
528 fragment_shader_->signature_source(),
533 TEST_F(MemoryProgramCacheTest, SaveCorrectProgram) {
534 const GLenum kFormat = 1;
535 const int kProgramId = 10;
536 const int kBinaryLength = 20;
537 char test_binary[kBinaryLength];
538 for (int i = 0; i < kBinaryLength; ++i) {
541 ProgramBinaryEmulator emulator1(kBinaryLength, kFormat, test_binary);
543 vertex_shader_->set_source("different!");
544 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator1);
545 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
546 fragment_shader_, NULL, NULL,
547 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
548 base::Unretained(this)));
550 EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
551 vertex_shader_->signature_source(),
553 fragment_shader_->signature_source(),
558 TEST_F(MemoryProgramCacheTest, LoadCorrectProgram) {
559 const GLenum kFormat = 1;
560 const int kProgramId = 10;
561 const int kBinaryLength = 20;
562 char test_binary[kBinaryLength];
563 for (int i = 0; i < kBinaryLength; ++i) {
566 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
568 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
569 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
570 fragment_shader_, NULL, NULL,
571 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
572 base::Unretained(this)));
574 EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
575 vertex_shader_->signature_source(),
577 fragment_shader_->signature_source(),
581 SetExpectationsForLoadLinkedProgram(kProgramId, &emulator);
583 fragment_shader_->set_source("different!");
584 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram(
591 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
592 base::Unretained(this))));
595 TEST_F(MemoryProgramCacheTest, OverwriteOnNewSave) {
596 const GLenum kFormat = 1;
597 const int kProgramId = 10;
598 const int kBinaryLength = 20;
599 char test_binary[kBinaryLength];
600 for (int i = 0; i < kBinaryLength; ++i) {
603 ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
605 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
606 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
607 fragment_shader_, NULL, NULL,
608 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
609 base::Unretained(this)));
612 char test_binary2[kBinaryLength];
613 for (int i = 0; i < kBinaryLength; ++i) {
614 test_binary2[i] = (i*2) % 250;
616 ProgramBinaryEmulator emulator2(kBinaryLength, kFormat, test_binary2);
617 SetExpectationsForSaveLinkedProgram(kProgramId, &emulator2);
618 cache_->SaveLinkedProgram(kProgramId, vertex_shader_, NULL,
619 fragment_shader_, NULL, NULL,
620 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
621 base::Unretained(this)));
623 SetExpectationsForLoadLinkedProgram(kProgramId, &emulator2);
624 EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram(
631 base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
632 base::Unretained(this))));