1// Copyright 2013 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/mailbox_manager.h" 6 7#include "gpu/command_buffer/service/feature_info.h" 8#include "gpu/command_buffer/service/gpu_service_test.h" 9#include "gpu/command_buffer/service/mailbox_synchronizer.h" 10#include "gpu/command_buffer/service/texture_manager.h" 11#include "testing/gtest/include/gtest/gtest.h" 12#include "ui/gl/gl_context_stub.h" 13#include "ui/gl/gl_mock.h" 14#include "ui/gl/gl_surface_stub.h" 15 16namespace gpu { 17namespace gles2 { 18 19using namespace ::testing; 20 21class MailboxManagerTest : public GpuServiceTest { 22 public: 23 MailboxManagerTest() : initialized_synchronizer_(false) {} 24 virtual ~MailboxManagerTest() {} 25 26 protected: 27 virtual void SetUp() { 28 GpuServiceTest::SetUp(); 29 feature_info_ = new FeatureInfo; 30 manager_ = new MailboxManager; 31 } 32 33 virtual void SetUpWithSynchronizer() { 34 GpuServiceTest::SetUp(); 35 MailboxSynchronizer::Initialize(); 36 initialized_synchronizer_ = true; 37 feature_info_ = new FeatureInfo; 38 manager_ = new MailboxManager; 39 } 40 41 virtual void TearDown() { 42 if (initialized_synchronizer_) 43 MailboxSynchronizer::Terminate(); 44 GpuServiceTest::TearDown(); 45 } 46 47 Texture* CreateTexture() { 48 return new Texture(1); 49 } 50 51 void SetTarget(Texture* texture, GLenum target, GLuint max_level) { 52 texture->SetTarget(NULL, target, max_level); 53 } 54 55 void SetLevelInfo( 56 Texture* texture, 57 GLenum target, 58 GLint level, 59 GLenum internal_format, 60 GLsizei width, 61 GLsizei height, 62 GLsizei depth, 63 GLint border, 64 GLenum format, 65 GLenum type, 66 bool cleared) { 67 texture->SetLevelInfo(NULL, 68 target, 69 level, 70 internal_format, 71 width, 72 height, 73 depth, 74 border, 75 format, 76 type, 77 cleared); 78 } 79 80 GLenum SetParameter(Texture* texture, GLenum pname, GLint param) { 81 return texture->SetParameteri(feature_info_, pname, param); 82 } 83 84 void DestroyTexture(Texture* texture) { 85 delete texture; 86 } 87 88 scoped_refptr<MailboxManager> manager_; 89 90 private: 91 bool initialized_synchronizer_; 92 scoped_refptr<FeatureInfo> feature_info_; 93 94 DISALLOW_COPY_AND_ASSIGN(MailboxManagerTest); 95}; 96 97// Tests basic produce/consume behavior. 98TEST_F(MailboxManagerTest, Basic) { 99 Texture* texture = CreateTexture(); 100 101 Mailbox name = Mailbox::Generate(); 102 manager_->ProduceTexture(0, name, texture); 103 EXPECT_EQ(texture, manager_->ConsumeTexture(0, name)); 104 105 // We can consume multiple times. 106 EXPECT_EQ(texture, manager_->ConsumeTexture(0, name)); 107 108 // Wrong target should fail the consume. 109 EXPECT_EQ(NULL, manager_->ConsumeTexture(1, name)); 110 111 // Destroy should cleanup the mailbox. 112 DestroyTexture(texture); 113 EXPECT_EQ(NULL, manager_->ConsumeTexture(0, name)); 114} 115 116// Tests behavior with multiple produce on the same texture. 117TEST_F(MailboxManagerTest, ProduceMultipleMailbox) { 118 Texture* texture = CreateTexture(); 119 120 Mailbox name1 = Mailbox::Generate(); 121 122 manager_->ProduceTexture(0, name1, texture); 123 EXPECT_EQ(texture, manager_->ConsumeTexture(0, name1)); 124 125 // Can produce a second time with the same mailbox. 126 manager_->ProduceTexture(0, name1, texture); 127 EXPECT_EQ(texture, manager_->ConsumeTexture(0, name1)); 128 129 // Can produce again, with a different mailbox. 130 Mailbox name2 = Mailbox::Generate(); 131 manager_->ProduceTexture(0, name2, texture); 132 133 // Still available under all mailboxes. 134 EXPECT_EQ(texture, manager_->ConsumeTexture(0, name1)); 135 EXPECT_EQ(texture, manager_->ConsumeTexture(0, name2)); 136 137 // Destroy should cleanup all mailboxes. 138 DestroyTexture(texture); 139 EXPECT_EQ(NULL, manager_->ConsumeTexture(0, name1)); 140 EXPECT_EQ(NULL, manager_->ConsumeTexture(0, name2)); 141} 142 143// Tests behavior with multiple produce on the same mailbox with different 144// textures. 145TEST_F(MailboxManagerTest, ProduceMultipleTexture) { 146 Texture* texture1 = CreateTexture(); 147 Texture* texture2 = CreateTexture(); 148 149 Mailbox name = Mailbox::Generate(); 150 151 manager_->ProduceTexture(0, name, texture1); 152 EXPECT_EQ(texture1, manager_->ConsumeTexture(0, name)); 153 154 // Can produce a second time with the same mailbox, but different texture. 155 manager_->ProduceTexture(0, name, texture2); 156 EXPECT_EQ(texture2, manager_->ConsumeTexture(0, name)); 157 158 // Destroying the texture that's under no mailbox shouldn't have an effect. 159 DestroyTexture(texture1); 160 EXPECT_EQ(texture2, manager_->ConsumeTexture(0, name)); 161 162 // Destroying the texture that's bound should clean up. 163 DestroyTexture(texture2); 164 EXPECT_EQ(NULL, manager_->ConsumeTexture(0, name)); 165} 166 167TEST_F(MailboxManagerTest, ProduceMultipleTextureMailbox) { 168 Texture* texture1 = CreateTexture(); 169 Texture* texture2 = CreateTexture(); 170 Mailbox name1 = Mailbox::Generate(); 171 Mailbox name2 = Mailbox::Generate(); 172 173 // Put texture1 on name1 and name2. 174 manager_->ProduceTexture(0, name1, texture1); 175 manager_->ProduceTexture(0, name2, texture1); 176 EXPECT_EQ(texture1, manager_->ConsumeTexture(0, name1)); 177 EXPECT_EQ(texture1, manager_->ConsumeTexture(0, name2)); 178 179 // Put texture2 on name2. 180 manager_->ProduceTexture(0, name2, texture2); 181 EXPECT_EQ(texture1, manager_->ConsumeTexture(0, name1)); 182 EXPECT_EQ(texture2, manager_->ConsumeTexture(0, name2)); 183 184 // Destroy texture1, shouldn't affect name2. 185 DestroyTexture(texture1); 186 EXPECT_EQ(NULL, manager_->ConsumeTexture(0, name1)); 187 EXPECT_EQ(texture2, manager_->ConsumeTexture(0, name2)); 188 189 DestroyTexture(texture2); 190 EXPECT_EQ(NULL, manager_->ConsumeTexture(0, name2)); 191} 192 193const GLsizei kMaxTextureWidth = 64; 194const GLsizei kMaxTextureHeight = 64; 195const GLsizei kMaxTextureDepth = 1; 196 197class MailboxManagerSyncTest : public MailboxManagerTest { 198 public: 199 MailboxManagerSyncTest() {} 200 virtual ~MailboxManagerSyncTest() {} 201 202 protected: 203 virtual void SetUp() { 204 MailboxManagerTest::SetUpWithSynchronizer(); 205 manager2_ = new MailboxManager; 206 context_ = new gfx::GLContextStub(); 207 surface_ = new gfx::GLSurfaceStub(); 208 context_->MakeCurrent(surface_); 209 } 210 211 Texture* DefineTexture() { 212 Texture* texture = CreateTexture(); 213 const GLsizei levels_needed = TextureManager::ComputeMipMapCount( 214 GL_TEXTURE_2D, kMaxTextureWidth, kMaxTextureHeight, kMaxTextureDepth); 215 SetTarget(texture, GL_TEXTURE_2D, levels_needed); 216 SetLevelInfo(texture, 217 GL_TEXTURE_2D, 218 0, 219 GL_RGBA, 220 1, 221 1, 222 1, 223 0, 224 GL_RGBA, 225 GL_UNSIGNED_BYTE, 226 true); 227 SetParameter(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 228 SetParameter(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 229 return texture; 230 } 231 232 void SetupUpdateTexParamExpectations(GLuint texture_id, 233 GLenum min, 234 GLenum mag, 235 GLenum wrap_s, 236 GLenum wrap_t) { 237 DCHECK(texture_id); 238 const GLuint kCurrentTexture = 0; 239 EXPECT_CALL(*gl_, GetIntegerv(GL_TEXTURE_BINDING_2D, _)) 240 .WillOnce(SetArgPointee<1>(kCurrentTexture)) 241 .RetiresOnSaturation(); 242 EXPECT_CALL(*gl_, BindTexture(GL_TEXTURE_2D, texture_id)) 243 .Times(1) 244 .RetiresOnSaturation(); 245 EXPECT_CALL(*gl_, 246 TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min)) 247 .Times(1) 248 .RetiresOnSaturation(); 249 EXPECT_CALL(*gl_, 250 TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag)) 251 .Times(1) 252 .RetiresOnSaturation(); 253 EXPECT_CALL(*gl_, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s)) 254 .Times(1) 255 .RetiresOnSaturation(); 256 EXPECT_CALL(*gl_, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t)) 257 .Times(1) 258 .RetiresOnSaturation(); 259 EXPECT_CALL(*gl_, Flush()) 260 .Times(1) 261 .RetiresOnSaturation(); 262 EXPECT_CALL(*gl_, BindTexture(GL_TEXTURE_2D, kCurrentTexture)) 263 .Times(1) 264 .RetiresOnSaturation(); 265 } 266 267 virtual void TearDown() { 268 context_->ReleaseCurrent(NULL); 269 MailboxManagerTest::TearDown(); 270 } 271 272 scoped_refptr<MailboxManager> manager2_; 273 scoped_refptr<gfx::GLContext> context_; 274 scoped_refptr<gfx::GLSurface> surface_; 275 276 private: 277 DISALLOW_COPY_AND_ASSIGN(MailboxManagerSyncTest); 278}; 279 280TEST_F(MailboxManagerSyncTest, ProduceDestroy) { 281 Texture* texture = DefineTexture(); 282 Mailbox name = Mailbox::Generate(); 283 284 InSequence sequence; 285 manager_->ProduceTexture(GL_TEXTURE_2D, name, texture); 286 EXPECT_EQ(texture, manager_->ConsumeTexture(GL_TEXTURE_2D, name)); 287 288 DestroyTexture(texture); 289 EXPECT_EQ(NULL, manager_->ConsumeTexture(GL_TEXTURE_2D, name)); 290 EXPECT_EQ(NULL, manager2_->ConsumeTexture(GL_TEXTURE_2D, name)); 291} 292 293TEST_F(MailboxManagerSyncTest, ProduceSyncDestroy) { 294 InSequence sequence; 295 296 Texture* texture = DefineTexture(); 297 Mailbox name = Mailbox::Generate(); 298 299 manager_->ProduceTexture(GL_TEXTURE_2D, name, texture); 300 EXPECT_EQ(texture, manager_->ConsumeTexture(GL_TEXTURE_2D, name)); 301 302 // Synchronize 303 manager_->PushTextureUpdates(); 304 manager2_->PullTextureUpdates(); 305 306 DestroyTexture(texture); 307 EXPECT_EQ(NULL, manager_->ConsumeTexture(GL_TEXTURE_2D, name)); 308 EXPECT_EQ(NULL, manager2_->ConsumeTexture(GL_TEXTURE_2D, name)); 309} 310 311// Duplicates a texture into a second manager instance, and then 312// makes sure a redefinition becomes visible there too. 313TEST_F(MailboxManagerSyncTest, ProduceConsumeResize) { 314 const GLuint kNewTextureId = 1234; 315 InSequence sequence; 316 317 Texture* texture = DefineTexture(); 318 Mailbox name = Mailbox::Generate(); 319 320 manager_->ProduceTexture(GL_TEXTURE_2D, name, texture); 321 EXPECT_EQ(texture, manager_->ConsumeTexture(GL_TEXTURE_2D, name)); 322 323 // Synchronize 324 manager_->PushTextureUpdates(); 325 manager2_->PullTextureUpdates(); 326 327 EXPECT_CALL(*gl_, GenTextures(1, _)) 328 .WillOnce(SetArgPointee<1>(kNewTextureId)); 329 SetupUpdateTexParamExpectations( 330 kNewTextureId, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT); 331 Texture* new_texture = manager2_->ConsumeTexture(GL_TEXTURE_2D, name); 332 EXPECT_FALSE(new_texture == NULL); 333 EXPECT_NE(texture, new_texture); 334 EXPECT_EQ(kNewTextureId, new_texture->service_id()); 335 336 // Resize original texture 337 SetLevelInfo(texture, 338 GL_TEXTURE_2D, 339 0, 340 GL_RGBA, 341 16, 342 32, 343 1, 344 0, 345 GL_RGBA, 346 GL_UNSIGNED_BYTE, 347 true); 348 // Should have been orphaned 349 EXPECT_TRUE(texture->GetLevelImage(GL_TEXTURE_2D, 0) == NULL); 350 351 // Synchronize again 352 manager_->PushTextureUpdates(); 353 SetupUpdateTexParamExpectations( 354 kNewTextureId, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT); 355 manager2_->PullTextureUpdates(); 356 GLsizei width, height; 357 new_texture->GetLevelSize(GL_TEXTURE_2D, 0, &width, &height); 358 EXPECT_EQ(16, width); 359 EXPECT_EQ(32, height); 360 361 // Should have gotten a new attachment 362 EXPECT_TRUE(texture->GetLevelImage(GL_TEXTURE_2D, 0) != NULL); 363 // Resize original texture again.... 364 SetLevelInfo(texture, 365 GL_TEXTURE_2D, 366 0, 367 GL_RGBA, 368 64, 369 64, 370 1, 371 0, 372 GL_RGBA, 373 GL_UNSIGNED_BYTE, 374 true); 375 // ...and immediately delete the texture which should save the changes. 376 SetupUpdateTexParamExpectations( 377 kNewTextureId, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT); 378 DestroyTexture(texture); 379 380 // Should be still around since there is a ref from manager2 381 EXPECT_EQ(new_texture, manager2_->ConsumeTexture(GL_TEXTURE_2D, name)); 382 383 // The last change to the texture should be visible without a sync point (i.e. 384 // push). 385 manager2_->PullTextureUpdates(); 386 new_texture->GetLevelSize(GL_TEXTURE_2D, 0, &width, &height); 387 EXPECT_EQ(64, width); 388 EXPECT_EQ(64, height); 389 390 DestroyTexture(new_texture); 391 EXPECT_EQ(NULL, manager_->ConsumeTexture(GL_TEXTURE_2D, name)); 392 EXPECT_EQ(NULL, manager2_->ConsumeTexture(GL_TEXTURE_2D, name)); 393} 394 395// Makes sure changes are correctly published even when updates are 396// pushed in both directions, i.e. makes sure we don't clobber a shared 397// texture definition with an older version. 398TEST_F(MailboxManagerSyncTest, ProduceConsumeBidirectional) { 399 const GLuint kNewTextureId1 = 1234; 400 const GLuint kNewTextureId2 = 4321; 401 402 Texture* texture1 = DefineTexture(); 403 Mailbox name1 = Mailbox::Generate(); 404 Texture* texture2 = DefineTexture(); 405 Mailbox name2 = Mailbox::Generate(); 406 Texture* new_texture1 = NULL; 407 Texture* new_texture2 = NULL; 408 409 manager_->ProduceTexture(GL_TEXTURE_2D, name1, texture1); 410 manager2_->ProduceTexture(GL_TEXTURE_2D, name2, texture2); 411 412 // Make visible. 413 manager_->PushTextureUpdates(); 414 manager2_->PushTextureUpdates(); 415 416 // Create textures in the other manager instances for texture1 and texture2, 417 // respectively to create a real sharing scenario. Otherwise, there would 418 // never be conflicting updates/pushes. 419 { 420 InSequence sequence; 421 EXPECT_CALL(*gl_, GenTextures(1, _)) 422 .WillOnce(SetArgPointee<1>(kNewTextureId1)); 423 SetupUpdateTexParamExpectations( 424 kNewTextureId1, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT); 425 new_texture1 = manager2_->ConsumeTexture(GL_TEXTURE_2D, name1); 426 EXPECT_CALL(*gl_, GenTextures(1, _)) 427 .WillOnce(SetArgPointee<1>(kNewTextureId2)); 428 SetupUpdateTexParamExpectations( 429 kNewTextureId2, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT); 430 new_texture2 = manager_->ConsumeTexture(GL_TEXTURE_2D, name2); 431 } 432 EXPECT_EQ(kNewTextureId1, new_texture1->service_id()); 433 EXPECT_EQ(kNewTextureId2, new_texture2->service_id()); 434 435 // Make a change to texture1 436 DCHECK_EQ(static_cast<GLuint>(GL_LINEAR), texture1->min_filter()); 437 EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), 438 SetParameter(texture1, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); 439 440 // Make sure this does not clobber it with the previous version we pushed. 441 manager_->PullTextureUpdates(); 442 443 // Make a change to texture2 444 DCHECK_EQ(static_cast<GLuint>(GL_LINEAR), texture2->mag_filter()); 445 EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), 446 SetParameter(texture2, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); 447 448 Mock::VerifyAndClearExpectations(gl_.get()); 449 450 // Synchronize in both directions 451 manager_->PushTextureUpdates(); 452 manager2_->PushTextureUpdates(); 453 // manager1 should see the change to texture2 mag_filter being applied. 454 SetupUpdateTexParamExpectations( 455 new_texture2->service_id(), GL_LINEAR, GL_NEAREST, GL_REPEAT, GL_REPEAT); 456 manager_->PullTextureUpdates(); 457 // manager2 should see the change to texture1 min_filter being applied. 458 SetupUpdateTexParamExpectations( 459 new_texture1->service_id(), GL_NEAREST, GL_LINEAR, GL_REPEAT, GL_REPEAT); 460 manager2_->PullTextureUpdates(); 461 462 DestroyTexture(texture1); 463 DestroyTexture(texture2); 464 DestroyTexture(new_texture1); 465 DestroyTexture(new_texture2); 466} 467 468// TODO: different texture into same mailbox 469 470// TODO: same texture, multiple mailboxes 471 472// TODO: Produce incomplete texture 473 474// TODO: Texture::level_infos_[][].size() 475 476// TODO: unsupported targets and formats 477 478} // namespace gles2 479} // namespace gpu 480