resource_provider_unittest.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
1// Copyright 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.
4
5#include "cc/resources/resource_provider.h"
6
7#include <algorithm>
8
9#include "base/bind.h"
10#include "base/containers/hash_tables.h"
11#include "base/logging.h"
12#include "base/memory/ref_counted.h"
13#include "cc/base/scoped_ptr_deque.h"
14#include "cc/output/output_surface.h"
15#include "cc/test/fake_output_surface.h"
16#include "cc/test/test_web_graphics_context_3d.h"
17#include "gpu/GLES2/gl2extchromium.h"
18#include "testing/gmock/include/gmock/gmock.h"
19#include "testing/gtest/include/gtest/gtest.h"
20#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
21#include "third_party/khronos/GLES2/gl2.h"
22#include "third_party/khronos/GLES2/gl2ext.h"
23#include "ui/gfx/rect.h"
24
25using testing::Mock;
26using testing::NiceMock;
27using testing::Return;
28using testing::SetArgPointee;
29using testing::StrictMock;
30using testing::_;
31using WebKit::WGC3Dbyte;
32using WebKit::WGC3Denum;
33using WebKit::WGC3Dint;
34using WebKit::WGC3Dsizei;
35using WebKit::WGC3Duint;
36using WebKit::WebGLId;
37
38namespace cc {
39namespace {
40
41size_t TextureSize(gfx::Size size, WGC3Denum format) {
42  unsigned int components_per_pixel = 4;
43  unsigned int bytes_per_component = 1;
44  return size.width() * size.height() * components_per_pixel *
45      bytes_per_component;
46}
47
48struct Texture : public base::RefCounted<Texture> {
49  Texture() : format(0), filter(GL_NEAREST_MIPMAP_LINEAR) {}
50
51  void Reallocate(gfx::Size size, WGC3Denum format) {
52    this->size = size;
53    this->format = format;
54    this->data.reset(new uint8_t[TextureSize(size, format)]);
55  }
56
57  gfx::Size size;
58  WGC3Denum format;
59  WGC3Denum filter;
60  scoped_ptr<uint8_t[]> data;
61
62 private:
63  friend class base::RefCounted<Texture>;
64  ~Texture() {}
65};
66
67// Shared data between multiple ResourceProviderContext. This contains mailbox
68// contents as well as information about sync points.
69class ContextSharedData {
70 public:
71  static scoped_ptr<ContextSharedData> Create() {
72    return make_scoped_ptr(new ContextSharedData());
73  }
74
75  unsigned InsertSyncPoint() { return next_sync_point_++; }
76
77  void GenMailbox(WGC3Dbyte* mailbox) {
78    memset(mailbox, 0, sizeof(WGC3Dbyte[64]));
79    memcpy(mailbox, &next_mailbox_, sizeof(next_mailbox_));
80    ++next_mailbox_;
81  }
82
83  void ProduceTexture(const WGC3Dbyte* mailbox_name,
84                      unsigned sync_point,
85                      scoped_refptr<Texture> texture) {
86    unsigned mailbox = 0;
87    memcpy(&mailbox, mailbox_name, sizeof(mailbox));
88    ASSERT_TRUE(mailbox && mailbox < next_mailbox_);
89    textures_[mailbox] = texture;
90    ASSERT_LT(sync_point_for_mailbox_[mailbox], sync_point);
91    sync_point_for_mailbox_[mailbox] = sync_point;
92  }
93
94  scoped_refptr<Texture> ConsumeTexture(const WGC3Dbyte* mailbox_name,
95                                     unsigned sync_point) {
96    unsigned mailbox = 0;
97    memcpy(&mailbox, mailbox_name, sizeof(mailbox));
98    DCHECK(mailbox && mailbox < next_mailbox_);
99
100    // If the latest sync point the context has waited on is before the sync
101    // point for when the mailbox was set, pretend we never saw that
102    // ProduceTexture.
103    if (sync_point_for_mailbox_[mailbox] > sync_point) {
104      NOTREACHED();
105      return scoped_refptr<Texture>();
106    }
107    return textures_[mailbox];
108  }
109
110 private:
111  ContextSharedData() : next_sync_point_(1), next_mailbox_(1) {}
112
113  unsigned next_sync_point_;
114  unsigned next_mailbox_;
115  typedef base::hash_map<unsigned, scoped_refptr<Texture> > TextureMap;
116  TextureMap textures_;
117  base::hash_map<unsigned, unsigned> sync_point_for_mailbox_;
118};
119
120class ResourceProviderContext : public TestWebGraphicsContext3D {
121 public:
122  static scoped_ptr<ResourceProviderContext> Create(
123      ContextSharedData* shared_data) {
124    return make_scoped_ptr(
125        new ResourceProviderContext(Attributes(), shared_data));
126  }
127
128  virtual unsigned insertSyncPoint() OVERRIDE {
129    unsigned sync_point = shared_data_->InsertSyncPoint();
130    // Commit the produceTextureCHROMIUM calls at this point, so that
131    // they're associated with the sync point.
132    for (PendingProduceTextureList::iterator it =
133             pending_produce_textures_.begin();
134         it != pending_produce_textures_.end();
135         ++it) {
136      shared_data_->ProduceTexture(
137          (*it)->mailbox, sync_point, (*it)->texture);
138    }
139    pending_produce_textures_.clear();
140    return sync_point;
141  }
142
143  virtual void waitSyncPoint(unsigned sync_point) OVERRIDE {
144    last_waited_sync_point_ = std::max(sync_point, last_waited_sync_point_);
145  }
146
147  virtual void bindTexture(WGC3Denum target, WebGLId texture) OVERRIDE {
148    ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
149    ASSERT_TRUE(!texture || textures_.find(texture) != textures_.end());
150    current_texture_ = texture;
151  }
152
153  virtual WebGLId createTexture() OVERRIDE {
154    WebGLId id = TestWebGraphicsContext3D::createTexture();
155    textures_[id] = new Texture;
156    return id;
157  }
158
159  virtual void deleteTexture(WebGLId id) OVERRIDE {
160    TextureMap::iterator it = textures_.find(id);
161    ASSERT_FALSE(it == textures_.end());
162    textures_.erase(it);
163    if (current_texture_ == id)
164      current_texture_ = 0;
165  }
166
167  virtual void texStorage2DEXT(WGC3Denum target,
168                               WGC3Dint levels,
169                               WGC3Duint internalformat,
170                               WGC3Dint width,
171                               WGC3Dint height) OVERRIDE {
172    ASSERT_TRUE(current_texture_);
173    ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
174    ASSERT_EQ(1, levels);
175    WGC3Denum format = GL_RGBA;
176    switch (internalformat) {
177      case GL_RGBA8_OES:
178        break;
179      case GL_BGRA8_EXT:
180        format = GL_BGRA_EXT;
181        break;
182      default:
183        NOTREACHED();
184    }
185    AllocateTexture(gfx::Size(width, height), format);
186  }
187
188  virtual void texImage2D(WGC3Denum target,
189                          WGC3Dint level,
190                          WGC3Denum internalformat,
191                          WGC3Dsizei width,
192                          WGC3Dsizei height,
193                          WGC3Dint border,
194                          WGC3Denum format,
195                          WGC3Denum type,
196                          const void* pixels) OVERRIDE {
197    ASSERT_TRUE(current_texture_);
198    ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
199    ASSERT_FALSE(level);
200    ASSERT_EQ(internalformat, format);
201    ASSERT_FALSE(border);
202    ASSERT_EQ(static_cast<unsigned>(GL_UNSIGNED_BYTE), type);
203    AllocateTexture(gfx::Size(width, height), format);
204    if (pixels)
205      SetPixels(0, 0, width, height, pixels);
206  }
207
208  virtual void texSubImage2D(WGC3Denum target,
209                             WGC3Dint level,
210                             WGC3Dint xoffset,
211                             WGC3Dint yoffset,
212                             WGC3Dsizei width,
213                             WGC3Dsizei height,
214                             WGC3Denum format,
215                             WGC3Denum type,
216                             const void* pixels) OVERRIDE {
217    ASSERT_TRUE(current_texture_);
218    ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
219    ASSERT_FALSE(level);
220    ASSERT_TRUE(textures_[current_texture_]);
221    ASSERT_EQ(textures_[current_texture_]->format, format);
222    ASSERT_EQ(static_cast<unsigned>(GL_UNSIGNED_BYTE), type);
223    ASSERT_TRUE(pixels);
224    SetPixels(xoffset, yoffset, width, height, pixels);
225  }
226
227  virtual void texParameteri(WGC3Denum target, WGC3Denum param, WGC3Dint value)
228      OVERRIDE {
229    ASSERT_TRUE(current_texture_);
230    ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
231    scoped_refptr<Texture> texture = textures_[current_texture_];
232    ASSERT_TRUE(texture);
233    if (param != GL_TEXTURE_MIN_FILTER)
234      return;
235    texture->filter = value;
236  }
237
238  virtual void genMailboxCHROMIUM(WGC3Dbyte* mailbox) OVERRIDE {
239    return shared_data_->GenMailbox(mailbox);
240  }
241
242  virtual void produceTextureCHROMIUM(WGC3Denum target,
243                                      const WGC3Dbyte* mailbox) OVERRIDE {
244    ASSERT_TRUE(current_texture_);
245    ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
246
247    // Delay moving the texture into the mailbox until the next
248    // InsertSyncPoint, so that it is not visible to other contexts that
249    // haven't waited on that sync point.
250    scoped_ptr<PendingProduceTexture> pending(new PendingProduceTexture);
251    memcpy(pending->mailbox, mailbox, sizeof(pending->mailbox));
252    pending->texture = textures_[current_texture_];
253    pending_produce_textures_.push_back(pending.Pass());
254  }
255
256  virtual void consumeTextureCHROMIUM(WGC3Denum target,
257                                      const WGC3Dbyte* mailbox) OVERRIDE {
258    ASSERT_TRUE(current_texture_);
259    ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
260    textures_[current_texture_] = shared_data_->ConsumeTexture(
261        mailbox, last_waited_sync_point_);
262  }
263
264  void GetPixels(gfx::Size size, WGC3Denum format, uint8_t* pixels) {
265    ASSERT_TRUE(current_texture_);
266    scoped_refptr<Texture> texture = textures_[current_texture_];
267    ASSERT_TRUE(texture);
268    ASSERT_EQ(texture->size, size);
269    ASSERT_EQ(texture->format, format);
270    memcpy(pixels, texture->data.get(), TextureSize(size, format));
271  }
272
273  WGC3Denum GetTextureFilter() {
274    DCHECK(current_texture_);
275    scoped_refptr<Texture> texture = textures_[current_texture_];
276    DCHECK(texture);
277    return texture->filter;
278  }
279
280  int texture_count() { return textures_.size(); }
281
282 protected:
283  ResourceProviderContext(const Attributes& attrs,
284                          ContextSharedData* shared_data)
285      : TestWebGraphicsContext3D(attrs),
286        shared_data_(shared_data),
287        current_texture_(0),
288        last_waited_sync_point_(0) {}
289
290 private:
291  void AllocateTexture(gfx::Size size, WGC3Denum format) {
292    ASSERT_TRUE(current_texture_);
293    scoped_refptr<Texture> texture = textures_[current_texture_];
294    ASSERT_TRUE(texture);
295    texture->Reallocate(size, format);
296  }
297
298  void SetPixels(int xoffset,
299                 int yoffset,
300                 int width,
301                 int height,
302                 const void* pixels) {
303    ASSERT_TRUE(current_texture_);
304    scoped_refptr<Texture> texture = textures_[current_texture_];
305    ASSERT_TRUE(texture);
306    ASSERT_TRUE(texture->data.get());
307    ASSERT_TRUE(xoffset >= 0 && xoffset + width <= texture->size.width());
308    ASSERT_TRUE(yoffset >= 0 && yoffset + height <= texture->size.height());
309    ASSERT_TRUE(pixels);
310    size_t in_pitch = TextureSize(gfx::Size(width, 1), texture->format);
311    size_t out_pitch =
312        TextureSize(gfx::Size(texture->size.width(), 1), texture->format);
313    uint8_t* dest = texture->data.get() + yoffset * out_pitch +
314                    TextureSize(gfx::Size(xoffset, 1), texture->format);
315    const uint8_t* src = static_cast<const uint8_t*>(pixels);
316    for (int i = 0; i < height; ++i) {
317      memcpy(dest, src, in_pitch);
318      dest += out_pitch;
319      src += in_pitch;
320    }
321  }
322
323  typedef base::hash_map<WebGLId, scoped_refptr<Texture> > TextureMap;
324  struct PendingProduceTexture {
325    WGC3Dbyte mailbox[64];
326    scoped_refptr<Texture> texture;
327  };
328  typedef ScopedPtrDeque<PendingProduceTexture> PendingProduceTextureList;
329  ContextSharedData* shared_data_;
330  WebGLId current_texture_;
331  TextureMap textures_;
332  unsigned last_waited_sync_point_;
333  PendingProduceTextureList pending_produce_textures_;
334};
335
336class ResourceProviderTest
337    : public testing::TestWithParam<ResourceProvider::ResourceType> {
338 public:
339  ResourceProviderTest()
340      : shared_data_(ContextSharedData::Create()),
341        output_surface_(FakeOutputSurface::Create3d(
342            ResourceProviderContext::Create(shared_data_.get())
343                .PassAs<WebKit::WebGraphicsContext3D>())),
344        resource_provider_(ResourceProvider::Create(output_surface_.get(), 0)) {
345    resource_provider_->set_default_resource_type(GetParam());
346  }
347
348  ResourceProviderContext* context() {
349    return static_cast<ResourceProviderContext*>(output_surface_->context3d());
350  }
351
352  void GetResourcePixels(ResourceProvider::ResourceId id,
353                         gfx::Size size,
354                         WGC3Denum format,
355                         uint8_t* pixels) {
356    if (GetParam() == ResourceProvider::GLTexture) {
357      ResourceProvider::ScopedReadLockGL lock_gl(resource_provider_.get(), id);
358      ASSERT_NE(0U, lock_gl.texture_id());
359      context()->bindTexture(GL_TEXTURE_2D, lock_gl.texture_id());
360      context()->GetPixels(size, format, pixels);
361    } else if (GetParam() == ResourceProvider::Bitmap) {
362      ResourceProvider::ScopedReadLockSoftware lock_software(
363          resource_provider_.get(), id);
364      memcpy(pixels,
365             lock_software.sk_bitmap()->getPixels(),
366             lock_software.sk_bitmap()->getSize());
367    }
368  }
369
370  void SetResourceFilter(ResourceProvider* resource_provider,
371                         ResourceProvider::ResourceId id,
372                         WGC3Denum filter) {
373    ResourceProvider::ScopedSamplerGL sampler(
374        resource_provider, id, GL_TEXTURE_2D, filter);
375  }
376
377  WGC3Denum GetResourceFilter(ResourceProvider* resource_provider,
378                              ResourceProvider::ResourceId id) {
379    DCHECK_EQ(GetParam(), ResourceProvider::GLTexture);
380    ResourceProvider::ScopedReadLockGL lock_gl(resource_provider, id);
381    EXPECT_NE(0u, lock_gl.texture_id());
382    ResourceProviderContext* context = static_cast<ResourceProviderContext*>(
383        resource_provider->GraphicsContext3D());
384    context->bindTexture(GL_TEXTURE_2D, lock_gl.texture_id());
385    return context->GetTextureFilter();
386  }
387
388 protected:
389  scoped_ptr<ContextSharedData> shared_data_;
390  scoped_ptr<OutputSurface> output_surface_;
391  scoped_ptr<ResourceProvider> resource_provider_;
392};
393
394TEST_P(ResourceProviderTest, Basic) {
395  gfx::Size size(1, 1);
396  WGC3Denum format = GL_RGBA;
397  size_t pixel_size = TextureSize(size, format);
398  ASSERT_EQ(4U, pixel_size);
399
400  ResourceProvider::ResourceId id = resource_provider_->CreateResource(
401      size, format, ResourceProvider::TextureUsageAny);
402  EXPECT_EQ(1, static_cast<int>(resource_provider_->num_resources()));
403  if (GetParam() == ResourceProvider::GLTexture)
404    EXPECT_EQ(0, context()->texture_count());
405
406  uint8_t data[4] = { 1, 2, 3, 4 };
407  gfx::Rect rect(size);
408  resource_provider_->SetPixels(id, data, rect, rect, gfx::Vector2d());
409  if (GetParam() == ResourceProvider::GLTexture)
410    EXPECT_EQ(1, context()->texture_count());
411
412  uint8_t result[4] = { 0 };
413  GetResourcePixels(id, size, format, result);
414  EXPECT_EQ(0, memcmp(data, result, pixel_size));
415
416  resource_provider_->DeleteResource(id);
417  EXPECT_EQ(0, static_cast<int>(resource_provider_->num_resources()));
418  if (GetParam() == ResourceProvider::GLTexture)
419    EXPECT_EQ(0, context()->texture_count());
420}
421
422TEST_P(ResourceProviderTest, Upload) {
423  gfx::Size size(2, 2);
424  WGC3Denum format = GL_RGBA;
425  size_t pixel_size = TextureSize(size, format);
426  ASSERT_EQ(16U, pixel_size);
427
428  ResourceProvider::ResourceId id = resource_provider_->CreateResource(
429      size, format, ResourceProvider::TextureUsageAny);
430
431  uint8_t image[16] = { 0 };
432  gfx::Rect image_rect(size);
433  resource_provider_->SetPixels(
434      id, image, image_rect, image_rect, gfx::Vector2d());
435
436  for (uint8_t i = 0; i < pixel_size; ++i)
437    image[i] = i;
438
439  uint8_t result[16] = { 0 };
440  {
441    gfx::Rect source_rect(0, 0, 1, 1);
442    gfx::Vector2d dest_offset(0, 0);
443    resource_provider_->SetPixels(
444        id, image, image_rect, source_rect, dest_offset);
445
446    uint8_t expected[16] = { 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
447    GetResourcePixels(id, size, format, result);
448    EXPECT_EQ(0, memcmp(expected, result, pixel_size));
449  }
450  {
451    gfx::Rect source_rect(0, 0, 1, 1);
452    gfx::Vector2d dest_offset(1, 1);
453    resource_provider_->SetPixels(
454        id, image, image_rect, source_rect, dest_offset);
455
456    uint8_t expected[16] = { 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3 };
457    GetResourcePixels(id, size, format, result);
458    EXPECT_EQ(0, memcmp(expected, result, pixel_size));
459  }
460  {
461    gfx::Rect source_rect(1, 0, 1, 1);
462    gfx::Vector2d dest_offset(0, 1);
463    resource_provider_->SetPixels(
464        id, image, image_rect, source_rect, dest_offset);
465
466    uint8_t expected[16] = { 0, 1, 2, 3, 0, 0, 0, 0, 4, 5, 6, 7, 0, 1, 2, 3 };
467    GetResourcePixels(id, size, format, result);
468    EXPECT_EQ(0, memcmp(expected, result, pixel_size));
469  }
470  {
471    gfx::Rect offset_image_rect(gfx::Point(100, 100), size);
472    gfx::Rect source_rect(100, 100, 1, 1);
473    gfx::Vector2d dest_offset(1, 0);
474    resource_provider_->SetPixels(
475        id, image, offset_image_rect, source_rect, dest_offset);
476
477    uint8_t expected[16] = { 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3 };
478    GetResourcePixels(id, size, format, result);
479    EXPECT_EQ(0, memcmp(expected, result, pixel_size));
480  }
481
482  resource_provider_->DeleteResource(id);
483}
484
485TEST_P(ResourceProviderTest, TransferResources) {
486  // Resource transfer is only supported with GL textures for now.
487  if (GetParam() != ResourceProvider::GLTexture)
488    return;
489
490  scoped_ptr<OutputSurface> child_output_surface(FakeOutputSurface::Create3d(
491      ResourceProviderContext::Create(shared_data_.get())
492          .PassAs<WebKit::WebGraphicsContext3D>()));
493  scoped_ptr<ResourceProvider> child_resource_provider(
494      ResourceProvider::Create(child_output_surface.get(), 0));
495
496  gfx::Size size(1, 1);
497  WGC3Denum format = GL_RGBA;
498  size_t pixel_size = TextureSize(size, format);
499  ASSERT_EQ(4U, pixel_size);
500
501  ResourceProvider::ResourceId id1 = child_resource_provider->CreateResource(
502      size, format, ResourceProvider::TextureUsageAny);
503  uint8_t data1[4] = { 1, 2, 3, 4 };
504  gfx::Rect rect(size);
505  child_resource_provider->SetPixels(id1, data1, rect, rect, gfx::Vector2d());
506
507  ResourceProvider::ResourceId id2 = child_resource_provider->CreateResource(
508      size, format, ResourceProvider::TextureUsageAny);
509  uint8_t data2[4] = { 5, 5, 5, 5 };
510  child_resource_provider->SetPixels(id2, data2, rect, rect, gfx::Vector2d());
511
512  int child_id = resource_provider_->CreateChild();
513  {
514    // Transfer some resources to the parent.
515    ResourceProvider::ResourceIdArray resource_ids_to_transfer;
516    resource_ids_to_transfer.push_back(id1);
517    resource_ids_to_transfer.push_back(id2);
518    TransferableResourceArray list;
519    child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
520                                                 &list);
521    ASSERT_EQ(2u, list.size());
522    EXPECT_NE(0u, list[0].sync_point);
523    EXPECT_NE(0u, list[1].sync_point);
524    EXPECT_TRUE(child_resource_provider->InUseByConsumer(id1));
525    EXPECT_TRUE(child_resource_provider->InUseByConsumer(id2));
526    resource_provider_->ReceiveFromChild(child_id, list);
527  }
528
529  EXPECT_EQ(2u, resource_provider_->num_resources());
530  ResourceProvider::ResourceIdMap resource_map =
531      resource_provider_->GetChildToParentMap(child_id);
532  ResourceProvider::ResourceId mapped_id1 = resource_map[id1];
533  ResourceProvider::ResourceId mapped_id2 = resource_map[id2];
534  EXPECT_NE(0u, mapped_id1);
535  EXPECT_NE(0u, mapped_id2);
536  EXPECT_FALSE(resource_provider_->InUseByConsumer(id1));
537  EXPECT_FALSE(resource_provider_->InUseByConsumer(id2));
538
539  uint8_t result[4] = { 0 };
540  GetResourcePixels(mapped_id1, size, format, result);
541  EXPECT_EQ(0, memcmp(data1, result, pixel_size));
542
543  GetResourcePixels(mapped_id2, size, format, result);
544  EXPECT_EQ(0, memcmp(data2, result, pixel_size));
545  {
546    // Check that transfering again the same resource from the child to the
547    // parent is a noop.
548    ResourceProvider::ResourceIdArray resource_ids_to_transfer;
549    resource_ids_to_transfer.push_back(id1);
550    TransferableResourceArray list;
551    child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
552                                                 &list);
553    EXPECT_EQ(0u, list.size());
554  }
555  {
556    // Transfer resources back from the parent to the child.
557    ResourceProvider::ResourceIdArray resource_ids_to_transfer;
558    resource_ids_to_transfer.push_back(mapped_id1);
559    resource_ids_to_transfer.push_back(mapped_id2);
560    TransferableResourceArray list;
561    resource_provider_->PrepareSendToChild(
562        child_id, resource_ids_to_transfer, &list);
563    ASSERT_EQ(2u, list.size());
564    EXPECT_NE(0u, list[0].sync_point);
565    EXPECT_NE(0u, list[1].sync_point);
566    child_resource_provider->ReceiveFromParent(list);
567  }
568  EXPECT_FALSE(child_resource_provider->InUseByConsumer(id1));
569  EXPECT_FALSE(child_resource_provider->InUseByConsumer(id2));
570
571  ResourceProviderContext* child_context =
572      static_cast<ResourceProviderContext*>(child_output_surface->context3d());
573  {
574    ResourceProvider::ScopedReadLockGL lock(child_resource_provider.get(), id1);
575    ASSERT_NE(0U, lock.texture_id());
576    child_context->bindTexture(GL_TEXTURE_2D, lock.texture_id());
577    child_context->GetPixels(size, format, result);
578    EXPECT_EQ(0, memcmp(data1, result, pixel_size));
579  }
580  {
581    ResourceProvider::ScopedReadLockGL lock(child_resource_provider.get(), id2);
582    ASSERT_NE(0U, lock.texture_id());
583    child_context->bindTexture(GL_TEXTURE_2D, lock.texture_id());
584    child_context->GetPixels(size, format, result);
585    EXPECT_EQ(0, memcmp(data2, result, pixel_size));
586  }
587  {
588    // Transfer resources to the parent again.
589    ResourceProvider::ResourceIdArray resource_ids_to_transfer;
590    resource_ids_to_transfer.push_back(id1);
591    resource_ids_to_transfer.push_back(id2);
592    TransferableResourceArray list;
593    child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
594                                                 &list);
595    ASSERT_EQ(2u, list.size());
596    EXPECT_NE(0u, list[0].sync_point);
597    EXPECT_NE(0u, list[1].sync_point);
598    EXPECT_TRUE(child_resource_provider->InUseByConsumer(id1));
599    EXPECT_TRUE(child_resource_provider->InUseByConsumer(id2));
600    resource_provider_->ReceiveFromChild(child_id, list);
601  }
602
603  EXPECT_EQ(2u, resource_provider_->num_resources());
604  resource_provider_->DestroyChild(child_id);
605  EXPECT_EQ(0u, resource_provider_->num_resources());
606}
607
608TEST_P(ResourceProviderTest, DeleteTransferredResources) {
609  // Resource transfer is only supported with GL textures for now.
610  if (GetParam() != ResourceProvider::GLTexture)
611    return;
612
613  scoped_ptr<OutputSurface> child_output_surface(FakeOutputSurface::Create3d(
614      ResourceProviderContext::Create(shared_data_.get())
615          .PassAs<WebKit::WebGraphicsContext3D>()));
616  scoped_ptr<ResourceProvider> child_resource_provider(
617      ResourceProvider::Create(child_output_surface.get(), 0));
618
619  gfx::Size size(1, 1);
620  WGC3Denum format = GL_RGBA;
621  size_t pixel_size = TextureSize(size, format);
622  ASSERT_EQ(4U, pixel_size);
623
624  ResourceProvider::ResourceId id = child_resource_provider->CreateResource(
625      size, format, ResourceProvider::TextureUsageAny);
626  uint8_t data[4] = { 1, 2, 3, 4 };
627  gfx::Rect rect(size);
628  child_resource_provider->SetPixels(id, data, rect, rect, gfx::Vector2d());
629
630  int child_id = resource_provider_->CreateChild();
631  {
632    // Transfer some resource to the parent.
633    ResourceProvider::ResourceIdArray resource_ids_to_transfer;
634    resource_ids_to_transfer.push_back(id);
635    TransferableResourceArray list;
636    child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
637                                                 &list);
638    ASSERT_EQ(1u, list.size());
639    EXPECT_NE(0u, list[0].sync_point);
640    EXPECT_TRUE(child_resource_provider->InUseByConsumer(id));
641    resource_provider_->ReceiveFromChild(child_id, list);
642  }
643
644  // Delete textures in the child, while they are transfered.
645  child_resource_provider->DeleteResource(id);
646  EXPECT_EQ(1u, child_resource_provider->num_resources());
647  {
648    // Transfer resources back from the parent to the child.
649    ResourceProvider::ResourceIdMap resource_map =
650        resource_provider_->GetChildToParentMap(child_id);
651    ResourceProvider::ResourceId mapped_id = resource_map[id];
652    EXPECT_NE(0u, mapped_id);
653    ResourceProvider::ResourceIdArray resource_ids_to_transfer;
654    resource_ids_to_transfer.push_back(mapped_id);
655    TransferableResourceArray list;
656    resource_provider_->PrepareSendToChild(
657        child_id, resource_ids_to_transfer, &list);
658    ASSERT_EQ(1u, list.size());
659    EXPECT_NE(0u, list[0].sync_point);
660    child_resource_provider->ReceiveFromParent(list);
661  }
662  EXPECT_EQ(0u, child_resource_provider->num_resources());
663}
664
665TEST_P(ResourceProviderTest, TextureFilters) {
666  // Resource transfer is only supported with GL textures for now.
667  if (GetParam() != ResourceProvider::GLTexture)
668    return;
669
670  scoped_ptr<OutputSurface> child_output_surface(FakeOutputSurface::Create3d(
671      ResourceProviderContext::Create(shared_data_.get())
672          .PassAs<WebKit::WebGraphicsContext3D>()));
673  scoped_ptr<ResourceProvider> child_resource_provider(
674      ResourceProvider::Create(child_output_surface.get(), 0));
675
676  gfx::Size size(1, 1);
677  WGC3Denum format = GL_RGBA;
678  size_t pixel_size = TextureSize(size, format);
679  ASSERT_EQ(4U, pixel_size);
680
681  ResourceProvider::ResourceId id = child_resource_provider->CreateResource(
682      size, format, ResourceProvider::TextureUsageAny);
683  uint8_t data[4] = { 1, 2, 3, 4 };
684  gfx::Rect rect(size);
685  child_resource_provider->SetPixels(id, data, rect, rect, gfx::Vector2d());
686  EXPECT_EQ(static_cast<unsigned>(GL_LINEAR),
687            GetResourceFilter(child_resource_provider.get(), id));
688  SetResourceFilter(child_resource_provider.get(), id, GL_NEAREST);
689  EXPECT_EQ(static_cast<unsigned>(GL_NEAREST),
690            GetResourceFilter(child_resource_provider.get(), id));
691
692  int child_id = resource_provider_->CreateChild();
693  {
694    // Transfer some resource to the parent.
695    ResourceProvider::ResourceIdArray resource_ids_to_transfer;
696    resource_ids_to_transfer.push_back(id);
697    TransferableResourceArray list;
698    child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
699                                                 &list);
700    ASSERT_EQ(1u, list.size());
701    EXPECT_EQ(static_cast<unsigned>(GL_NEAREST), list[0].filter);
702    resource_provider_->ReceiveFromChild(child_id, list);
703  }
704  ResourceProvider::ResourceIdMap resource_map =
705      resource_provider_->GetChildToParentMap(child_id);
706  ResourceProvider::ResourceId mapped_id = resource_map[id];
707  EXPECT_NE(0u, mapped_id);
708  EXPECT_EQ(static_cast<unsigned>(GL_NEAREST),
709            GetResourceFilter(resource_provider_.get(), mapped_id));
710  SetResourceFilter(resource_provider_.get(), mapped_id, GL_LINEAR);
711  EXPECT_EQ(static_cast<unsigned>(GL_LINEAR),
712            GetResourceFilter(resource_provider_.get(), mapped_id));
713  {
714    // Transfer resources back from the parent to the child.
715    ResourceProvider::ResourceIdArray resource_ids_to_transfer;
716    resource_ids_to_transfer.push_back(mapped_id);
717    TransferableResourceArray list;
718    resource_provider_->PrepareSendToChild(
719        child_id, resource_ids_to_transfer, &list);
720    ASSERT_EQ(1u, list.size());
721    EXPECT_EQ(static_cast<unsigned>(GL_LINEAR), list[0].filter);
722    child_resource_provider->ReceiveFromParent(list);
723  }
724  EXPECT_EQ(static_cast<unsigned>(GL_LINEAR),
725            GetResourceFilter(child_resource_provider.get(), id));
726  SetResourceFilter(child_resource_provider.get(), id, GL_NEAREST);
727  EXPECT_EQ(static_cast<unsigned>(GL_NEAREST),
728            GetResourceFilter(child_resource_provider.get(), id));
729}
730
731void ReleaseTextureMailbox(unsigned* release_sync_point,
732                           bool* release_lost_resource,
733                           unsigned sync_point,
734                           bool lost_resource) {
735  *release_sync_point = sync_point;
736  *release_lost_resource = lost_resource;
737}
738
739TEST_P(ResourceProviderTest, TransferMailboxResources) {
740  // Resource transfer is only supported with GL textures for now.
741  if (GetParam() != ResourceProvider::GLTexture)
742    return;
743  unsigned texture = context()->createTexture();
744  context()->bindTexture(GL_TEXTURE_2D, texture);
745  uint8_t data[4] = { 1, 2, 3, 4 };
746  context()->texImage2D(
747      GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &data);
748  gpu::Mailbox mailbox;
749  context()->genMailboxCHROMIUM(mailbox.name);
750  context()->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
751  unsigned sync_point = context()->insertSyncPoint();
752
753  // All the logic below assumes that the sync points are all positive.
754  EXPECT_LT(0u, sync_point);
755
756  unsigned release_sync_point = 0;
757  bool lost_resource = false;
758  TextureMailbox::ReleaseCallback callback =
759      base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource);
760  ResourceProvider::ResourceId resource =
761      resource_provider_->CreateResourceFromTextureMailbox(
762          TextureMailbox(mailbox, callback, sync_point));
763  EXPECT_EQ(1, context()->texture_count());
764  EXPECT_EQ(0u, release_sync_point);
765  {
766    // Transfer the resource, expect the sync points to be consistent.
767    ResourceProvider::ResourceIdArray resource_ids_to_transfer;
768    resource_ids_to_transfer.push_back(resource);
769    TransferableResourceArray list;
770    resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list);
771    ASSERT_EQ(1u, list.size());
772    EXPECT_LE(sync_point, list[0].sync_point);
773    EXPECT_EQ(0,
774              memcmp(mailbox.name, list[0].mailbox.name, sizeof(mailbox.name)));
775    EXPECT_EQ(0u, release_sync_point);
776
777    context()->waitSyncPoint(list[0].sync_point);
778    unsigned other_texture = context()->createTexture();
779    context()->bindTexture(GL_TEXTURE_2D, other_texture);
780    context()->consumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
781    uint8_t test_data[4] = { 0 };
782    context()->GetPixels(gfx::Size(1, 1), GL_RGBA, test_data);
783    EXPECT_EQ(0, memcmp(data, test_data, sizeof(data)));
784    context()->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
785    context()->deleteTexture(other_texture);
786    list[0].sync_point = context()->insertSyncPoint();
787    EXPECT_LT(0u, list[0].sync_point);
788
789    // Receive the resource, then delete it, expect the sync points to be
790    // consistent.
791    resource_provider_->ReceiveFromParent(list);
792    EXPECT_EQ(1, context()->texture_count());
793    EXPECT_EQ(0u, release_sync_point);
794
795    resource_provider_->DeleteResource(resource);
796    EXPECT_LE(list[0].sync_point, release_sync_point);
797    EXPECT_FALSE(lost_resource);
798  }
799
800  // We're going to do the same thing as above, but testing the case where we
801  // delete the resource before we receive it back.
802  sync_point = release_sync_point;
803  EXPECT_LT(0u, sync_point);
804  release_sync_point = 0;
805  resource = resource_provider_->CreateResourceFromTextureMailbox(
806      TextureMailbox(mailbox, callback, sync_point));
807  EXPECT_EQ(1, context()->texture_count());
808  EXPECT_EQ(0u, release_sync_point);
809  {
810    // Transfer the resource, expect the sync points to be consistent.
811    ResourceProvider::ResourceIdArray resource_ids_to_transfer;
812    resource_ids_to_transfer.push_back(resource);
813    TransferableResourceArray list;
814    resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list);
815    ASSERT_EQ(1u, list.size());
816    EXPECT_LE(sync_point, list[0].sync_point);
817    EXPECT_EQ(0,
818              memcmp(mailbox.name, list[0].mailbox.name, sizeof(mailbox.name)));
819    EXPECT_EQ(0u, release_sync_point);
820
821    context()->waitSyncPoint(list[0].sync_point);
822    unsigned other_texture = context()->createTexture();
823    context()->bindTexture(GL_TEXTURE_2D, other_texture);
824    context()->consumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
825    uint8_t test_data[4] = { 0 };
826    context()->GetPixels(gfx::Size(1, 1), GL_RGBA, test_data);
827    EXPECT_EQ(0, memcmp(data, test_data, sizeof(data)));
828    context()->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
829    context()->deleteTexture(other_texture);
830    list[0].sync_point = context()->insertSyncPoint();
831    EXPECT_LT(0u, list[0].sync_point);
832
833    // Delete the resource, which shouldn't do anything.
834    resource_provider_->DeleteResource(resource);
835    EXPECT_EQ(1, context()->texture_count());
836    EXPECT_EQ(0u, release_sync_point);
837
838    // Then receive the resource which should release the mailbox, expect the
839    // sync points to be consistent.
840    resource_provider_->ReceiveFromParent(list);
841    EXPECT_LE(list[0].sync_point, release_sync_point);
842    EXPECT_FALSE(lost_resource);
843  }
844
845  context()->waitSyncPoint(release_sync_point);
846  context()->bindTexture(GL_TEXTURE_2D, texture);
847  context()->consumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
848  context()->deleteTexture(texture);
849}
850
851TEST_P(ResourceProviderTest, Shutdown) {
852  // TextureMailbox callbacks only exist for GL textures for now.
853  if (GetParam() != ResourceProvider::GLTexture)
854    return;
855  unsigned texture = context()->createTexture();
856  context()->bindTexture(GL_TEXTURE_2D, texture);
857  gpu::Mailbox mailbox;
858  context()->genMailboxCHROMIUM(mailbox.name);
859  context()->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
860  unsigned sync_point = context()->insertSyncPoint();
861
862  EXPECT_LT(0u, sync_point);
863
864  unsigned release_sync_point = 0;
865  bool lost_resource = false;
866  TextureMailbox::ReleaseCallback callback =
867      base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource);
868  resource_provider_->CreateResourceFromTextureMailbox(
869      TextureMailbox(mailbox, callback, sync_point));
870
871  EXPECT_EQ(0u, release_sync_point);
872  EXPECT_FALSE(lost_resource);
873
874  resource_provider_.reset();
875
876  EXPECT_LE(sync_point, release_sync_point);
877  EXPECT_FALSE(lost_resource);
878}
879
880static scoped_ptr<base::SharedMemory> CreateAndFillSharedMemory(
881    gfx::Size size, uint32_t value) {
882  scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory);
883  CHECK(shared_memory->CreateAndMapAnonymous(4 * size.GetArea()));
884  uint32_t* pixels = reinterpret_cast<uint32_t*>(shared_memory->memory());
885  CHECK(pixels);
886  std::fill_n(pixels, size.GetArea(), value);
887  return shared_memory.Pass();
888}
889
890static void ReleaseSharedMemoryCallback(
891    bool* release_called,
892    unsigned sync_point, bool lost_resource) {
893  *release_called = true;
894}
895
896TEST_P(ResourceProviderTest, ShutdownSharedMemory) {
897  if (GetParam() != ResourceProvider::Bitmap)
898    return;
899
900  gfx::Size size(64, 64);
901  scoped_ptr<base::SharedMemory> shared_memory(
902      CreateAndFillSharedMemory(size, 0));
903
904  bool release_called = false;
905  TextureMailbox::ReleaseCallback callback =
906      base::Bind(ReleaseSharedMemoryCallback, &release_called);
907  resource_provider_->CreateResourceFromTextureMailbox(
908      TextureMailbox(shared_memory.get(), size, callback));
909
910  resource_provider_.reset();
911
912  EXPECT_TRUE(release_called);
913}
914
915TEST_P(ResourceProviderTest, ShutdownWithExportedResource) {
916  // TextureMailbox callbacks only exist for GL textures for now.
917  if (GetParam() != ResourceProvider::GLTexture)
918    return;
919  unsigned texture = context()->createTexture();
920  context()->bindTexture(GL_TEXTURE_2D, texture);
921  gpu::Mailbox mailbox;
922  context()->genMailboxCHROMIUM(mailbox.name);
923  context()->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
924  unsigned sync_point = context()->insertSyncPoint();
925
926  EXPECT_LT(0u, sync_point);
927
928  unsigned release_sync_point = 0;
929  bool lost_resource = false;
930  TextureMailbox::ReleaseCallback callback =
931      base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource);
932  ResourceProvider::ResourceId resource =
933      resource_provider_->CreateResourceFromTextureMailbox(
934          TextureMailbox(mailbox, callback, sync_point));
935
936  // Transfer the resource, so we can't release it properly on shutdown.
937  ResourceProvider::ResourceIdArray resource_ids_to_transfer;
938  resource_ids_to_transfer.push_back(resource);
939  TransferableResourceArray list;
940  resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list);
941
942  EXPECT_EQ(0u, release_sync_point);
943  EXPECT_FALSE(lost_resource);
944
945  resource_provider_.reset();
946
947  // Since the resource is in the parent, the child considers it lost.
948  EXPECT_EQ(0u, release_sync_point);
949  EXPECT_TRUE(lost_resource);
950}
951
952TEST_P(ResourceProviderTest, LostContext) {
953  // TextureMailbox callbacks only exist for GL textures for now.
954  if (GetParam() != ResourceProvider::GLTexture)
955    return;
956  unsigned texture = context()->createTexture();
957  context()->bindTexture(GL_TEXTURE_2D, texture);
958  gpu::Mailbox mailbox;
959  context()->genMailboxCHROMIUM(mailbox.name);
960  context()->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
961  unsigned sync_point = context()->insertSyncPoint();
962
963  EXPECT_LT(0u, sync_point);
964
965  unsigned release_sync_point = 0;
966  bool lost_resource = false;
967  TextureMailbox::ReleaseCallback callback =
968      base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource);
969  resource_provider_->CreateResourceFromTextureMailbox(
970      TextureMailbox(mailbox, callback, sync_point));
971
972  EXPECT_EQ(0u, release_sync_point);
973  EXPECT_FALSE(lost_resource);
974
975  resource_provider_->DidLoseOutputSurface();
976  resource_provider_.reset();
977
978  EXPECT_LE(sync_point, release_sync_point);
979  EXPECT_TRUE(lost_resource);
980}
981
982class TextureStateTrackingContext : public TestWebGraphicsContext3D {
983 public:
984  MOCK_METHOD2(bindTexture, void(WGC3Denum target, WebGLId texture));
985  MOCK_METHOD3(texParameteri,
986               void(WGC3Denum target, WGC3Denum pname, WGC3Dint param));
987  MOCK_METHOD1(waitSyncPoint, void(unsigned sync_point));
988  MOCK_METHOD0(insertSyncPoint, unsigned(void));
989  MOCK_METHOD2(produceTextureCHROMIUM, void(WGC3Denum target,
990                                            const WGC3Dbyte* mailbox));
991  MOCK_METHOD2(consumeTextureCHROMIUM, void(WGC3Denum target,
992                                            const WGC3Dbyte* mailbox));
993
994  // Force all textures to be "1" so we can test for them.
995  virtual WebKit::WebGLId NextTextureId() OVERRIDE { return 1; }
996};
997
998TEST_P(ResourceProviderTest, ScopedSampler) {
999  // Sampling is only supported for GL textures.
1000  if (GetParam() != ResourceProvider::GLTexture)
1001    return;
1002
1003  scoped_ptr<OutputSurface> output_surface(
1004      FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
1005          new TextureStateTrackingContext)));
1006  TextureStateTrackingContext* context =
1007      static_cast<TextureStateTrackingContext*>(output_surface->context3d());
1008  scoped_ptr<ResourceProvider> resource_provider(
1009      ResourceProvider::Create(output_surface.get(), 0));
1010
1011  gfx::Size size(1, 1);
1012  WGC3Denum format = GL_RGBA;
1013  int texture_id = 1;
1014
1015  // Check that the texture gets created with the right sampler settings.
1016  EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id))
1017      .Times(2);  // Once to create and once to allocate.
1018  EXPECT_CALL(*context,
1019              texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
1020  EXPECT_CALL(*context,
1021              texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
1022  EXPECT_CALL(
1023      *context,
1024      texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
1025  EXPECT_CALL(
1026      *context,
1027      texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
1028  EXPECT_CALL(*context,
1029              texParameteri(GL_TEXTURE_2D,
1030                            GL_TEXTURE_POOL_CHROMIUM,
1031                            GL_TEXTURE_POOL_UNMANAGED_CHROMIUM));
1032  ResourceProvider::ResourceId id = resource_provider->CreateResource(
1033      size, format, ResourceProvider::TextureUsageAny);
1034  resource_provider->AllocateForTesting(id);
1035
1036  // Creating a sampler with the default filter should not change any texture
1037  // parameters.
1038  {
1039    EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
1040    ResourceProvider::ScopedSamplerGL sampler(
1041        resource_provider.get(), id, GL_TEXTURE_2D, GL_LINEAR);
1042  }
1043
1044  // Using a different filter should be reflected in the texture parameters.
1045  {
1046    EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
1047    EXPECT_CALL(
1048        *context,
1049        texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
1050    EXPECT_CALL(
1051        *context,
1052        texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
1053    ResourceProvider::ScopedSamplerGL sampler(
1054        resource_provider.get(), id, GL_TEXTURE_2D, GL_NEAREST);
1055  }
1056
1057  // Test resetting to the default filter.
1058  {
1059    EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
1060    EXPECT_CALL(*context,
1061                texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
1062    EXPECT_CALL(*context,
1063                texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
1064    ResourceProvider::ScopedSamplerGL sampler(
1065        resource_provider.get(), id, GL_TEXTURE_2D, GL_LINEAR);
1066  }
1067
1068  Mock::VerifyAndClearExpectations(context);
1069}
1070
1071TEST_P(ResourceProviderTest, ManagedResource) {
1072  // Sampling is only supported for GL textures.
1073  if (GetParam() != ResourceProvider::GLTexture)
1074    return;
1075
1076  scoped_ptr<OutputSurface> output_surface(
1077      FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
1078          new TextureStateTrackingContext)));
1079  TextureStateTrackingContext* context =
1080      static_cast<TextureStateTrackingContext*>(output_surface->context3d());
1081  scoped_ptr<ResourceProvider> resource_provider(
1082      ResourceProvider::Create(output_surface.get(), 0));
1083
1084  gfx::Size size(1, 1);
1085  WGC3Denum format = GL_RGBA;
1086  int texture_id = 1;
1087
1088  // Check that the texture gets created with the right sampler settings.
1089  ResourceProvider::ResourceId id = resource_provider->CreateManagedResource(
1090      size, format, ResourceProvider::TextureUsageAny);
1091  EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
1092  EXPECT_CALL(*context,
1093              texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
1094  EXPECT_CALL(*context,
1095              texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
1096  EXPECT_CALL(
1097      *context,
1098      texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
1099  EXPECT_CALL(
1100      *context,
1101      texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
1102  EXPECT_CALL(*context,
1103              texParameteri(GL_TEXTURE_2D,
1104                            GL_TEXTURE_POOL_CHROMIUM,
1105                            GL_TEXTURE_POOL_MANAGED_CHROMIUM));
1106  resource_provider->CreateForTesting(id);
1107  EXPECT_NE(0u, id);
1108
1109  Mock::VerifyAndClearExpectations(context);
1110}
1111
1112static void EmptyReleaseCallback(unsigned sync_point, bool lost_resource) {}
1113
1114TEST_P(ResourceProviderTest, TextureMailbox_SharedMemory) {
1115  if (GetParam() != ResourceProvider::Bitmap)
1116    return;
1117
1118  gfx::Size size(64, 64);
1119  const uint32_t kBadBeef = 0xbadbeef;
1120  scoped_ptr<base::SharedMemory> shared_memory(
1121      CreateAndFillSharedMemory(size, kBadBeef));
1122
1123  scoped_ptr<OutputSurface> output_surface(
1124      FakeOutputSurface::CreateSoftware(make_scoped_ptr(
1125          new SoftwareOutputDevice)));
1126  scoped_ptr<ResourceProvider> resource_provider(
1127      ResourceProvider::Create(output_surface.get(), 0));
1128
1129  TextureMailbox::ReleaseCallback callback = base::Bind(&EmptyReleaseCallback);
1130  TextureMailbox mailbox(shared_memory.get(), size, callback);
1131
1132  ResourceProvider::ResourceId id =
1133      resource_provider->CreateResourceFromTextureMailbox(mailbox);
1134  EXPECT_NE(0u, id);
1135
1136  {
1137    ResourceProvider::ScopedReadLockSoftware lock(resource_provider.get(), id);
1138    const SkBitmap* sk_bitmap = lock.sk_bitmap();
1139    EXPECT_EQ(sk_bitmap->width(), size.width());
1140    EXPECT_EQ(sk_bitmap->height(), size.height());
1141    EXPECT_EQ(*sk_bitmap->getAddr32(16, 16), kBadBeef);
1142  }
1143}
1144
1145TEST_P(ResourceProviderTest, TextureMailbox_GLTexture2D) {
1146  // Mailboxing is only supported for GL textures.
1147  if (GetParam() != ResourceProvider::GLTexture)
1148    return;
1149
1150  scoped_ptr<OutputSurface> output_surface(
1151      FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
1152          new TextureStateTrackingContext)));
1153  TextureStateTrackingContext* context =
1154      static_cast<TextureStateTrackingContext*>(output_surface->context3d());
1155  scoped_ptr<ResourceProvider> resource_provider(
1156      ResourceProvider::Create(output_surface.get(), 0));
1157
1158  unsigned texture_id = 1;
1159  unsigned sync_point = 30;
1160  unsigned target = GL_TEXTURE_2D;
1161
1162  EXPECT_CALL(*context, bindTexture(_, _)).Times(0);
1163  EXPECT_CALL(*context, waitSyncPoint(_)).Times(0);
1164  EXPECT_CALL(*context, insertSyncPoint()).Times(0);
1165  EXPECT_CALL(*context, produceTextureCHROMIUM(_, _)).Times(0);
1166  EXPECT_CALL(*context, consumeTextureCHROMIUM(_, _)).Times(0);
1167
1168  gpu::Mailbox gpu_mailbox;
1169  memcpy(gpu_mailbox.name, "Hello world", strlen("Hello world") + 1);
1170  TextureMailbox::ReleaseCallback callback = base::Bind(&EmptyReleaseCallback);
1171
1172  TextureMailbox mailbox(gpu_mailbox,
1173                         callback,
1174                         sync_point);
1175
1176  ResourceProvider::ResourceId id =
1177      resource_provider->CreateResourceFromTextureMailbox(mailbox);
1178  EXPECT_NE(0u, id);
1179
1180  Mock::VerifyAndClearExpectations(context);
1181
1182  {
1183    // Using the texture does a consume of the mailbox.
1184    EXPECT_CALL(*context, bindTexture(target, texture_id));
1185    EXPECT_CALL(*context, waitSyncPoint(sync_point));
1186    EXPECT_CALL(*context, consumeTextureCHROMIUM(target, _));
1187
1188    EXPECT_CALL(*context, insertSyncPoint()).Times(0);
1189    EXPECT_CALL(*context, produceTextureCHROMIUM(_, _)).Times(0);
1190
1191    ResourceProvider::ScopedReadLockGL lock(resource_provider.get(), id);
1192    Mock::VerifyAndClearExpectations(context);
1193
1194    // When done with it, a sync point should be inserted, but no produce is
1195    // necessary.
1196    EXPECT_CALL(*context, bindTexture(_, _)).Times(0);
1197    EXPECT_CALL(*context, insertSyncPoint());
1198    EXPECT_CALL(*context, produceTextureCHROMIUM(_, _)).Times(0);
1199
1200    EXPECT_CALL(*context, waitSyncPoint(_)).Times(0);
1201    EXPECT_CALL(*context, consumeTextureCHROMIUM(_, _)).Times(0);
1202  }
1203}
1204
1205TEST_P(ResourceProviderTest, TextureMailbox_GLTextureExternalOES) {
1206  // Mailboxing is only supported for GL textures.
1207  if (GetParam() != ResourceProvider::GLTexture)
1208    return;
1209
1210  scoped_ptr<OutputSurface> output_surface(
1211      FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
1212          new TextureStateTrackingContext)));
1213  TextureStateTrackingContext* context =
1214      static_cast<TextureStateTrackingContext*>(output_surface->context3d());
1215  scoped_ptr<ResourceProvider> resource_provider(
1216      ResourceProvider::Create(output_surface.get(), 0));
1217
1218  unsigned texture_id = 1;
1219  unsigned sync_point = 30;
1220  unsigned target = GL_TEXTURE_EXTERNAL_OES;
1221
1222  EXPECT_CALL(*context, bindTexture(_, _)).Times(0);
1223  EXPECT_CALL(*context, waitSyncPoint(_)).Times(0);
1224  EXPECT_CALL(*context, insertSyncPoint()).Times(0);
1225  EXPECT_CALL(*context, produceTextureCHROMIUM(_, _)).Times(0);
1226  EXPECT_CALL(*context, consumeTextureCHROMIUM(_, _)).Times(0);
1227
1228  gpu::Mailbox gpu_mailbox;
1229  memcpy(gpu_mailbox.name, "Hello world", strlen("Hello world") + 1);
1230  TextureMailbox::ReleaseCallback callback = base::Bind(&EmptyReleaseCallback);
1231
1232  TextureMailbox mailbox(gpu_mailbox,
1233                         callback,
1234                         target,
1235                         sync_point);
1236
1237  ResourceProvider::ResourceId id =
1238      resource_provider->CreateResourceFromTextureMailbox(mailbox);
1239  EXPECT_NE(0u, id);
1240
1241  Mock::VerifyAndClearExpectations(context);
1242
1243  {
1244    // Using the texture does a consume of the mailbox.
1245    EXPECT_CALL(*context, bindTexture(target, texture_id));
1246    EXPECT_CALL(*context, waitSyncPoint(sync_point));
1247    EXPECT_CALL(*context, consumeTextureCHROMIUM(target, _));
1248
1249    EXPECT_CALL(*context, insertSyncPoint()).Times(0);
1250    EXPECT_CALL(*context, produceTextureCHROMIUM(_, _)).Times(0);
1251
1252    ResourceProvider::ScopedReadLockGL lock(resource_provider.get(), id);
1253    Mock::VerifyAndClearExpectations(context);
1254
1255    // When done with it, a sync point should be inserted, but no produce is
1256    // necessary.
1257    EXPECT_CALL(*context, bindTexture(_, _)).Times(0);
1258    EXPECT_CALL(*context, insertSyncPoint());
1259    EXPECT_CALL(*context, produceTextureCHROMIUM(_, _)).Times(0);
1260
1261    EXPECT_CALL(*context, waitSyncPoint(_)).Times(0);
1262    EXPECT_CALL(*context, consumeTextureCHROMIUM(_, _)).Times(0);
1263  }
1264}
1265
1266class AllocationTrackingContext3D : public TestWebGraphicsContext3D {
1267 public:
1268  MOCK_METHOD0(createTexture, WebGLId());
1269  MOCK_METHOD1(deleteTexture, void(WebGLId texture_id));
1270  MOCK_METHOD2(bindTexture, void(WGC3Denum target, WebGLId texture));
1271  MOCK_METHOD9(texImage2D,
1272               void(WGC3Denum target,
1273                    WGC3Dint level,
1274                    WGC3Denum internalformat,
1275                    WGC3Dsizei width,
1276                    WGC3Dsizei height,
1277                    WGC3Dint border,
1278                    WGC3Denum format,
1279                    WGC3Denum type,
1280                    const void* pixels));
1281  MOCK_METHOD9(texSubImage2D,
1282               void(WGC3Denum target,
1283                    WGC3Dint level,
1284                    WGC3Dint xoffset,
1285                    WGC3Dint yoffset,
1286                    WGC3Dsizei width,
1287                    WGC3Dsizei height,
1288                    WGC3Denum format,
1289                    WGC3Denum type,
1290                    const void* pixels));
1291  MOCK_METHOD9(asyncTexImage2DCHROMIUM,
1292               void(WGC3Denum target,
1293                    WGC3Dint level,
1294                    WGC3Denum internalformat,
1295                    WGC3Dsizei width,
1296                    WGC3Dsizei height,
1297                    WGC3Dint border,
1298                    WGC3Denum format,
1299                    WGC3Denum type,
1300                    const void* pixels));
1301  MOCK_METHOD9(asyncTexSubImage2DCHROMIUM,
1302               void(WGC3Denum target,
1303                    WGC3Dint level,
1304                    WGC3Dint xoffset,
1305                    WGC3Dint yoffset,
1306                    WGC3Dsizei width,
1307                    WGC3Dsizei height,
1308                    WGC3Denum format,
1309                    WGC3Denum type,
1310                    const void* pixels));
1311  MOCK_METHOD1(waitAsyncTexImage2DCHROMIUM, void(WGC3Denum));
1312  MOCK_METHOD3(createImageCHROMIUM, WGC3Duint(WGC3Dsizei, WGC3Dsizei,
1313                                              WGC3Denum));
1314  MOCK_METHOD1(destroyImageCHROMIUM, void(WGC3Duint));
1315  MOCK_METHOD2(mapImageCHROMIUM, void*(WGC3Duint, WGC3Denum));
1316  MOCK_METHOD3(getImageParameterivCHROMIUM, void(WGC3Duint, WGC3Denum,
1317                                                 GLint*));
1318  MOCK_METHOD1(unmapImageCHROMIUM, void(WGC3Duint));
1319  MOCK_METHOD2(bindTexImage2DCHROMIUM, void(WGC3Denum, WGC3Dint));
1320};
1321
1322TEST_P(ResourceProviderTest, TextureAllocation) {
1323  // Only for GL textures.
1324  if (GetParam() != ResourceProvider::GLTexture)
1325    return;
1326  scoped_ptr<WebKit::WebGraphicsContext3D> mock_context(
1327      static_cast<WebKit::WebGraphicsContext3D*>(
1328          new StrictMock<AllocationTrackingContext3D>));
1329  scoped_ptr<OutputSurface> output_surface(
1330      FakeOutputSurface::Create3d(mock_context.Pass()));
1331
1332  gfx::Size size(2, 2);
1333  gfx::Vector2d offset(0, 0);
1334  gfx::Rect rect(0, 0, 2, 2);
1335  WGC3Denum format = GL_RGBA;
1336  ResourceProvider::ResourceId id = 0;
1337  uint8_t pixels[16] = { 0 };
1338  int texture_id = 123;
1339
1340  AllocationTrackingContext3D* context =
1341      static_cast<AllocationTrackingContext3D*>(output_surface->context3d());
1342  scoped_ptr<ResourceProvider> resource_provider(
1343      ResourceProvider::Create(output_surface.get(), 0));
1344
1345  // Lazy allocation. Don't allocate when creating the resource.
1346  id = resource_provider->CreateResource(
1347      size, format, ResourceProvider::TextureUsageAny);
1348
1349  EXPECT_CALL(*context, createTexture()).WillOnce(Return(texture_id));
1350  EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(1);
1351  resource_provider->CreateForTesting(id);
1352
1353  EXPECT_CALL(*context, deleteTexture(texture_id)).Times(1);
1354  resource_provider->DeleteResource(id);
1355
1356  Mock::VerifyAndClearExpectations(context);
1357
1358  // Do allocate when we set the pixels.
1359  id = resource_provider->CreateResource(
1360      size, format, ResourceProvider::TextureUsageAny);
1361
1362  EXPECT_CALL(*context, createTexture()).WillOnce(Return(texture_id));
1363  EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(3);
1364  EXPECT_CALL(*context, texImage2D(_, _, _, 2, 2, _, _, _, _)).Times(1);
1365  EXPECT_CALL(*context, texSubImage2D(_, _, _, _, 2, 2, _, _, _)).Times(1);
1366  resource_provider->SetPixels(id, pixels, rect, rect, offset);
1367
1368  EXPECT_CALL(*context, deleteTexture(texture_id)).Times(1);
1369  resource_provider->DeleteResource(id);
1370
1371  Mock::VerifyAndClearExpectations(context);
1372
1373  // Same for SetPixelsFromBuffer
1374  id = resource_provider->CreateResource(
1375      size, format, ResourceProvider::TextureUsageAny);
1376  resource_provider->AcquirePixelBuffer(id);
1377
1378  EXPECT_CALL(*context, createTexture()).WillOnce(Return(texture_id));
1379  EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(3);
1380  EXPECT_CALL(*context, texImage2D(_, _, _, 2, 2, _, _, _, _)).Times(1);
1381  EXPECT_CALL(*context, texSubImage2D(_, _, _, _, 2, 2, _, _, _)).Times(1);
1382  resource_provider->SetPixelsFromBuffer(id);
1383
1384  resource_provider->ReleasePixelBuffer(id);
1385
1386  EXPECT_CALL(*context, deleteTexture(texture_id)).Times(1);
1387  resource_provider->DeleteResource(id);
1388
1389  Mock::VerifyAndClearExpectations(context);
1390
1391  // Same for async version.
1392  id = resource_provider->CreateResource(
1393      size, format, ResourceProvider::TextureUsageAny);
1394  resource_provider->AcquirePixelBuffer(id);
1395
1396  EXPECT_CALL(*context, createTexture()).WillOnce(Return(texture_id));
1397  EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(2);
1398  EXPECT_CALL(*context, asyncTexImage2DCHROMIUM(_, _, _, 2, 2, _, _, _, _))
1399      .Times(1);
1400  resource_provider->BeginSetPixels(id);
1401  ASSERT_TRUE(resource_provider->DidSetPixelsComplete(id));
1402
1403  resource_provider->ReleasePixelBuffer(id);
1404
1405  EXPECT_CALL(*context, deleteTexture(texture_id)).Times(1);
1406  resource_provider->DeleteResource(id);
1407
1408  Mock::VerifyAndClearExpectations(context);
1409}
1410
1411TEST_P(ResourceProviderTest, ForcingAsyncUploadToComplete) {
1412  // Only for GL textures.
1413  if (GetParam() != ResourceProvider::GLTexture)
1414    return;
1415  scoped_ptr<WebKit::WebGraphicsContext3D> mock_context(
1416      static_cast<WebKit::WebGraphicsContext3D*>(
1417          new StrictMock<AllocationTrackingContext3D>));
1418  scoped_ptr<OutputSurface> output_surface(
1419      FakeOutputSurface::Create3d(mock_context.Pass()));
1420
1421  gfx::Size size(2, 2);
1422  WGC3Denum format = GL_RGBA;
1423  ResourceProvider::ResourceId id = 0;
1424  int texture_id = 123;
1425
1426  AllocationTrackingContext3D* context =
1427      static_cast<AllocationTrackingContext3D*>(output_surface->context3d());
1428  scoped_ptr<ResourceProvider> resource_provider(
1429      ResourceProvider::Create(output_surface.get(), 0));
1430
1431  id = resource_provider->CreateResource(
1432      size, format, ResourceProvider::TextureUsageAny);
1433  resource_provider->AcquirePixelBuffer(id);
1434
1435  EXPECT_CALL(*context, createTexture()).WillOnce(Return(texture_id));
1436  EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(2);
1437  EXPECT_CALL(*context, asyncTexImage2DCHROMIUM(_, _, _, 2, 2, _, _, _, _))
1438      .Times(1);
1439  resource_provider->BeginSetPixels(id);
1440
1441  EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(1);
1442  EXPECT_CALL(*context, waitAsyncTexImage2DCHROMIUM(GL_TEXTURE_2D)).Times(1);
1443  EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, 0)).Times(1);
1444  resource_provider->ForceSetPixelsToComplete(id);
1445
1446  resource_provider->ReleasePixelBuffer(id);
1447
1448  EXPECT_CALL(*context, deleteTexture(texture_id)).Times(1);
1449  resource_provider->DeleteResource(id);
1450
1451  Mock::VerifyAndClearExpectations(context);
1452}
1453
1454TEST_P(ResourceProviderTest, PixelBufferLostContext) {
1455  scoped_ptr<WebKit::WebGraphicsContext3D> mock_context(
1456      static_cast<WebKit::WebGraphicsContext3D*>(
1457          new NiceMock<AllocationTrackingContext3D>));
1458  scoped_ptr<OutputSurface> output_surface(
1459      FakeOutputSurface::Create3d(mock_context.Pass()));
1460
1461  gfx::Size size(2, 2);
1462  WGC3Denum format = GL_RGBA;
1463  ResourceProvider::ResourceId id = 0;
1464  int texture_id = 123;
1465
1466  AllocationTrackingContext3D* context =
1467      static_cast<AllocationTrackingContext3D*>(output_surface->context3d());
1468  scoped_ptr<ResourceProvider> resource_provider(
1469      ResourceProvider::Create(output_surface.get(), 0));
1470
1471  EXPECT_CALL(*context, createTexture()).WillRepeatedly(Return(texture_id));
1472
1473  id = resource_provider->CreateResource(
1474      size, format, ResourceProvider::TextureUsageAny);
1475  context->loseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB,
1476                               GL_INNOCENT_CONTEXT_RESET_ARB);
1477  resource_provider->AcquirePixelBuffer(id);
1478  uint8_t* buffer = resource_provider->MapPixelBuffer(id);
1479  EXPECT_TRUE(buffer == NULL);
1480  resource_provider->UnmapPixelBuffer(id);
1481  resource_provider->ReleasePixelBuffer(id);
1482  Mock::VerifyAndClearExpectations(context);
1483}
1484
1485TEST_P(ResourceProviderTest, GpuMemoryBuffers) {
1486  // Only for GL textures.
1487  if (GetParam() != ResourceProvider::GLTexture)
1488    return;
1489  scoped_ptr<WebKit::WebGraphicsContext3D> mock_context(
1490      static_cast<WebKit::WebGraphicsContext3D*>(
1491          new StrictMock<AllocationTrackingContext3D>));
1492  scoped_ptr<OutputSurface> output_surface(
1493      FakeOutputSurface::Create3d(mock_context.Pass()));
1494
1495  const int kWidth = 2;
1496  const int kHeight = 2;
1497  gfx::Size size(kWidth, kHeight);
1498  WGC3Denum format = GL_RGBA;
1499  ResourceProvider::ResourceId id = 0;
1500  const unsigned kTextureId = 123u;
1501  const unsigned kImageId = 234u;
1502
1503  AllocationTrackingContext3D* context =
1504      static_cast<AllocationTrackingContext3D*>(output_surface->context3d());
1505  scoped_ptr<ResourceProvider> resource_provider(
1506      ResourceProvider::Create(output_surface.get(), 0));
1507
1508  id = resource_provider->CreateResource(
1509      size, format, ResourceProvider::TextureUsageAny);
1510  EXPECT_CALL(*context, createImageCHROMIUM(kWidth, kHeight, GL_RGBA8_OES))
1511      .WillOnce(Return(kImageId))
1512      .RetiresOnSaturation();
1513  resource_provider->AcquireImage(id);
1514
1515  EXPECT_CALL(*context, createTexture())
1516      .WillOnce(Return(kTextureId))
1517      .RetiresOnSaturation();
1518  EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, kTextureId)).Times(2)
1519      .RetiresOnSaturation();
1520  EXPECT_CALL(*context, bindTexImage2DCHROMIUM(GL_TEXTURE_2D, kImageId))
1521      .Times(1)
1522      .RetiresOnSaturation();
1523  resource_provider->BindImage(id);
1524
1525  void* dummy_mapped_buffer_address = NULL;
1526  EXPECT_CALL(*context, mapImageCHROMIUM(kImageId, GL_READ_WRITE))
1527      .WillOnce(Return(dummy_mapped_buffer_address))
1528      .RetiresOnSaturation();
1529  resource_provider->MapImage(id);
1530
1531  const int kStride = 4;
1532  EXPECT_CALL(*context, getImageParameterivCHROMIUM(kImageId,
1533                                                    GL_IMAGE_ROWBYTES_CHROMIUM,
1534                                                    _))
1535      .WillOnce(SetArgPointee<2>(kStride))
1536      .RetiresOnSaturation();
1537  int stride = resource_provider->GetImageStride(id);
1538  EXPECT_EQ(kStride, stride);
1539
1540  EXPECT_CALL(*context, unmapImageCHROMIUM(kImageId))
1541      .Times(1)
1542      .RetiresOnSaturation();
1543  resource_provider->UnmapImage(id);
1544
1545  EXPECT_CALL(*context, destroyImageCHROMIUM(kImageId))
1546      .Times(1)
1547      .RetiresOnSaturation();
1548  resource_provider->ReleaseImage(id);
1549
1550  // Texture will be deleted when ResourceProvider destructor is
1551  // called when it goes out of scope when this method returns.
1552  EXPECT_CALL(*context, deleteTexture(kTextureId))
1553      .Times(1)
1554      .RetiresOnSaturation();
1555}
1556
1557INSTANTIATE_TEST_CASE_P(
1558    ResourceProviderTests,
1559    ResourceProviderTest,
1560    ::testing::Values(ResourceProvider::GLTexture, ResourceProvider::Bitmap));
1561
1562}  // namespace
1563}  // namespace cc
1564