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