layer_tree_pixel_test.cc revision 558790d6acca3451cf3a6b497803a5f07d0bec58
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 "cc/test/layer_tree_pixel_test.h"
6
7#include "base/path_service.h"
8#include "cc/layers/solid_color_layer.h"
9#include "cc/layers/texture_layer.h"
10#include "cc/output/copy_output_request.h"
11#include "cc/output/copy_output_result.h"
12#include "cc/resources/texture_mailbox.h"
13#include "cc/test/paths.h"
14#include "cc/test/pixel_comparator.h"
15#include "cc/test/pixel_test_output_surface.h"
16#include "cc/test/pixel_test_software_output_device.h"
17#include "cc/test/pixel_test_utils.h"
18#include "cc/trees/layer_tree_impl.h"
19#include "ui/gl/gl_implementation.h"
20#include "webkit/common/gpu/context_provider_in_process.h"
21#include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h"
22
23namespace cc {
24
25LayerTreePixelTest::LayerTreePixelTest()
26    : pixel_comparator_(new ExactPixelComparator(true)),
27      test_type_(GL_WITH_DEFAULT),
28      pending_texture_mailbox_callbacks_(0) {}
29
30LayerTreePixelTest::~LayerTreePixelTest() {}
31
32scoped_ptr<OutputSurface> LayerTreePixelTest::CreateOutputSurface() {
33  gfx::Vector2d viewport_offset(20, 10);
34  gfx::Size surface_expansion_size(40, 60);
35  scoped_ptr<PixelTestOutputSurface> output_surface;
36
37  switch (test_type_) {
38    case SOFTWARE_WITH_DEFAULT:
39    case SOFTWARE_WITH_BITMAP: {
40      scoped_ptr<PixelTestSoftwareOutputDevice> software_output_device(
41          new PixelTestSoftwareOutputDevice);
42      software_output_device->set_surface_expansion_size(
43          surface_expansion_size);
44      output_surface = make_scoped_ptr(
45          new PixelTestOutputSurface(
46              software_output_device.PassAs<SoftwareOutputDevice>()));
47      break;
48    }
49
50    case GL_WITH_DEFAULT:
51    case GL_WITH_BITMAP: {
52      CHECK(gfx::InitializeGLBindings(gfx::kGLImplementationOSMesaGL));
53
54      using WebKit::WebGraphicsContext3D;
55      using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl;
56      output_surface = make_scoped_ptr(new PixelTestOutputSurface(
57          WebGraphicsContext3DInProcessCommandBufferImpl::
58              CreateOffscreenContext(WebGraphicsContext3D::Attributes())));
59      break;
60    }
61  }
62
63  output_surface->set_viewport_offset(viewport_offset);
64  output_surface->set_surface_expansion_size(surface_expansion_size);
65  return output_surface.PassAs<OutputSurface>();
66}
67
68scoped_refptr<cc::ContextProvider>
69LayerTreePixelTest::OffscreenContextProviderForMainThread() {
70  scoped_refptr<webkit::gpu::ContextProviderInProcess> provider =
71      webkit::gpu::ContextProviderInProcess::Create();
72  CHECK(provider->BindToCurrentThread());
73  return provider;
74}
75
76scoped_refptr<cc::ContextProvider>
77LayerTreePixelTest::OffscreenContextProviderForCompositorThread() {
78  scoped_refptr<webkit::gpu::ContextProviderInProcess> provider =
79      webkit::gpu::ContextProviderInProcess::Create();
80  CHECK(provider.get());
81  return provider;
82}
83
84scoped_ptr<CopyOutputRequest> LayerTreePixelTest::CreateCopyOutputRequest() {
85  return CopyOutputRequest::CreateBitmapRequest(
86      base::Bind(&LayerTreePixelTest::ReadbackResult, base::Unretained(this)));
87}
88
89void LayerTreePixelTest::ReadbackResult(scoped_ptr<CopyOutputResult> result) {
90  ASSERT_TRUE(result->HasBitmap());
91  result_bitmap_ = result->TakeBitmap().Pass();
92  EndTest();
93}
94
95void LayerTreePixelTest::BeginTest() {
96  Layer* target = readback_target_ ? readback_target_
97                                   : layer_tree_host()->root_layer();
98  target->RequestCopyOfOutput(CreateCopyOutputRequest().Pass());
99  PostSetNeedsCommitToMainThread();
100}
101
102void LayerTreePixelTest::AfterTest() {
103  base::FilePath test_data_dir;
104  EXPECT_TRUE(PathService::Get(cc::DIR_TEST_DATA, &test_data_dir));
105  base::FilePath ref_file_path = test_data_dir.Append(ref_file_);
106
107  // To rebaseline:
108  // EXPECT_TRUE(WritePNGFile(*result_bitmap_, ref_file_path, true));
109
110  EXPECT_TRUE(MatchesPNGFile(*result_bitmap_,
111                             ref_file_path,
112                             *pixel_comparator_));
113}
114
115scoped_refptr<SolidColorLayer> LayerTreePixelTest::CreateSolidColorLayer(
116    gfx::Rect rect, SkColor color) {
117  scoped_refptr<SolidColorLayer> layer = SolidColorLayer::Create();
118  layer->SetIsDrawable(true);
119  layer->SetAnchorPoint(gfx::PointF());
120  layer->SetBounds(rect.size());
121  layer->SetPosition(rect.origin());
122  layer->SetBackgroundColor(color);
123  return layer;
124}
125
126void LayerTreePixelTest::EndTest() {
127  // Drop TextureMailboxes on the main thread so that they can be cleaned up and
128  // the pending callbacks will fire.
129  for (size_t i = 0; i < texture_layers_.size(); ++i)
130    texture_layers_[i]->SetTextureMailbox(TextureMailbox());
131
132  TryEndTest();
133}
134
135void LayerTreePixelTest::TryEndTest() {
136  if (!result_bitmap_)
137    return;
138  if (pending_texture_mailbox_callbacks_)
139    return;
140  LayerTreeTest::EndTest();
141}
142
143scoped_refptr<SolidColorLayer> LayerTreePixelTest::
144    CreateSolidColorLayerWithBorder(
145        gfx::Rect rect, SkColor color, int border_width, SkColor border_color) {
146  scoped_refptr<SolidColorLayer> layer = CreateSolidColorLayer(rect, color);
147  scoped_refptr<SolidColorLayer> border_top = CreateSolidColorLayer(
148      gfx::Rect(0, 0, rect.width(), border_width), border_color);
149  scoped_refptr<SolidColorLayer> border_left = CreateSolidColorLayer(
150      gfx::Rect(0,
151                border_width,
152                border_width,
153                rect.height() - border_width * 2),
154      border_color);
155  scoped_refptr<SolidColorLayer> border_right = CreateSolidColorLayer(
156      gfx::Rect(rect.width() - border_width,
157                border_width,
158                border_width,
159                rect.height() - border_width * 2),
160      border_color);
161  scoped_refptr<SolidColorLayer> border_bottom = CreateSolidColorLayer(
162      gfx::Rect(0, rect.height() - border_width, rect.width(), border_width),
163      border_color);
164  layer->AddChild(border_top);
165  layer->AddChild(border_left);
166  layer->AddChild(border_right);
167  layer->AddChild(border_bottom);
168  return layer;
169}
170
171scoped_refptr<TextureLayer> LayerTreePixelTest::CreateTextureLayer(
172    gfx::Rect rect, const SkBitmap& bitmap) {
173  scoped_refptr<TextureLayer> layer = TextureLayer::CreateForMailbox(NULL);
174  layer->SetIsDrawable(true);
175  layer->SetAnchorPoint(gfx::PointF());
176  layer->SetBounds(rect.size());
177  layer->SetPosition(rect.origin());
178  layer->SetTextureMailbox(CopyBitmapToTextureMailboxAsTexture(bitmap));
179
180  texture_layers_.push_back(layer);
181  pending_texture_mailbox_callbacks_++;
182  return layer;
183}
184
185void LayerTreePixelTest::RunPixelTest(
186    PixelTestType test_type,
187    scoped_refptr<Layer> content_root,
188    base::FilePath file_name) {
189  test_type_ = test_type;
190  content_root_ = content_root;
191  readback_target_ = NULL;
192  ref_file_ = file_name;
193  RunTest(true, false, true);
194}
195
196void LayerTreePixelTest::RunPixelTestWithReadbackTarget(
197    PixelTestType test_type,
198    scoped_refptr<Layer> content_root,
199    Layer* target,
200    base::FilePath file_name) {
201  test_type_ = test_type;
202  content_root_ = content_root;
203  readback_target_ = target;
204  ref_file_ = file_name;
205  RunTest(true, false, true);
206}
207
208void LayerTreePixelTest::SetupTree() {
209  scoped_refptr<Layer> root = Layer::Create();
210  root->SetBounds(content_root_->bounds());
211  root->AddChild(content_root_);
212  layer_tree_host()->SetRootLayer(root);
213  LayerTreeTest::SetupTree();
214}
215
216scoped_ptr<SkBitmap> LayerTreePixelTest::CopyTextureMailboxToBitmap(
217    gfx::Size size,
218    const TextureMailbox& texture_mailbox) {
219  DCHECK(texture_mailbox.IsTexture());
220  if (!texture_mailbox.IsTexture())
221    return scoped_ptr<SkBitmap>();
222
223  using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl;
224  scoped_ptr<WebKit::WebGraphicsContext3D> context3d(
225      WebGraphicsContext3DInProcessCommandBufferImpl::CreateOffscreenContext(
226          WebKit::WebGraphicsContext3D::Attributes()));
227
228  EXPECT_TRUE(context3d->makeContextCurrent());
229
230  if (texture_mailbox.sync_point())
231    context3d->waitSyncPoint(texture_mailbox.sync_point());
232
233  unsigned texture_id = context3d->createTexture();
234  context3d->bindTexture(GL_TEXTURE_2D, texture_id);
235  context3d->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
236  context3d->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
237  context3d->texParameteri(
238      GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
239  context3d->texParameteri(
240      GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
241  context3d->consumeTextureCHROMIUM(texture_mailbox.target(),
242                                    texture_mailbox.data());
243  context3d->bindTexture(GL_TEXTURE_2D, 0);
244
245  unsigned fbo = context3d->createFramebuffer();
246  context3d->bindFramebuffer(GL_FRAMEBUFFER, fbo);
247  context3d->framebufferTexture2D(GL_FRAMEBUFFER,
248                                  GL_COLOR_ATTACHMENT0,
249                                  GL_TEXTURE_2D,
250                                  texture_id,
251                                  0);
252  EXPECT_EQ(static_cast<unsigned>(GL_FRAMEBUFFER_COMPLETE),
253            context3d->checkFramebufferStatus(GL_FRAMEBUFFER));
254
255  scoped_ptr<uint8[]> pixels(new uint8[size.GetArea() * 4]);
256  context3d->readPixels(0,
257                        0,
258                        size.width(),
259                        size.height(),
260                        GL_RGBA,
261                        GL_UNSIGNED_BYTE,
262                        pixels.get());
263
264  context3d->deleteFramebuffer(fbo);
265  context3d->deleteTexture(texture_id);
266
267  scoped_ptr<SkBitmap> bitmap(new SkBitmap);
268  bitmap->setConfig(SkBitmap::kARGB_8888_Config,
269                    size.width(),
270                    size.height());
271  bitmap->allocPixels();
272
273  scoped_ptr<SkAutoLockPixels> lock(new SkAutoLockPixels(*bitmap));
274  uint8* out_pixels = static_cast<uint8*>(bitmap->getPixels());
275
276  size_t row_bytes = size.width() * 4;
277  size_t total_bytes = size.height() * row_bytes;
278  for (size_t dest_y = 0; dest_y < total_bytes; dest_y += row_bytes) {
279    // Flip Y axis.
280    size_t src_y = total_bytes - dest_y - row_bytes;
281    // Swizzle OpenGL -> Skia byte order.
282    for (size_t x = 0; x < row_bytes; x += 4) {
283      out_pixels[dest_y + x + SK_R32_SHIFT/8] = pixels.get()[src_y + x + 0];
284      out_pixels[dest_y + x + SK_G32_SHIFT/8] = pixels.get()[src_y + x + 1];
285      out_pixels[dest_y + x + SK_B32_SHIFT/8] = pixels.get()[src_y + x + 2];
286      out_pixels[dest_y + x + SK_A32_SHIFT/8] = pixels.get()[src_y + x + 3];
287    }
288  }
289
290  return bitmap.Pass();
291}
292
293void LayerTreePixelTest::ReleaseTextureMailbox(
294    scoped_ptr<WebKit::WebGraphicsContext3D> context3d,
295    uint32 texture,
296    uint32 sync_point,
297    bool lost_resource) {
298  if (sync_point)
299    context3d->waitSyncPoint(sync_point);
300  context3d->deleteTexture(texture);
301  pending_texture_mailbox_callbacks_--;
302  TryEndTest();
303}
304
305TextureMailbox LayerTreePixelTest::CopyBitmapToTextureMailboxAsTexture(
306    const SkBitmap& bitmap) {
307  DCHECK_GT(bitmap.width(), 0);
308  DCHECK_GT(bitmap.height(), 0);
309
310  CHECK(gfx::InitializeGLBindings(gfx::kGLImplementationOSMesaGL));
311
312  using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl;
313  scoped_ptr<WebKit::WebGraphicsContext3D> context3d(
314      WebGraphicsContext3DInProcessCommandBufferImpl::CreateOffscreenContext(
315          WebKit::WebGraphicsContext3D::Attributes()));
316
317  EXPECT_TRUE(context3d->makeContextCurrent());
318
319  unsigned texture_id = context3d->createTexture();
320  context3d->bindTexture(GL_TEXTURE_2D, texture_id);
321  context3d->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
322  context3d->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
323  context3d->texParameteri(
324      GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
325  context3d->texParameteri(
326      GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
327
328  DCHECK_EQ(SkBitmap::kARGB_8888_Config, bitmap.getConfig());
329
330  {
331    SkAutoLockPixels lock(bitmap);
332
333    size_t row_bytes = bitmap.width() * 4;
334    size_t total_bytes = bitmap.height() * row_bytes;
335
336    scoped_ptr<uint8[]> gl_pixels(new uint8[total_bytes]);
337    uint8* bitmap_pixels = static_cast<uint8*>(bitmap.getPixels());
338
339    for (size_t y = 0; y < total_bytes; y += row_bytes) {
340      // Flip Y axis.
341      size_t src_y = total_bytes - y - row_bytes;
342      // Swizzle Skia -> OpenGL byte order.
343      for (size_t x = 0; x < row_bytes; x += 4) {
344        gl_pixels.get()[y + x + 0] = bitmap_pixels[src_y + x + SK_R32_SHIFT/8];
345        gl_pixels.get()[y + x + 1] = bitmap_pixels[src_y + x + SK_G32_SHIFT/8];
346        gl_pixels.get()[y + x + 2] = bitmap_pixels[src_y + x + SK_B32_SHIFT/8];
347        gl_pixels.get()[y + x + 3] = bitmap_pixels[src_y + x + SK_A32_SHIFT/8];
348      }
349    }
350
351    context3d->texImage2D(GL_TEXTURE_2D,
352                          0,
353                          GL_RGBA,
354                          bitmap.width(),
355                          bitmap.height(),
356                          0,
357                          GL_RGBA,
358                          GL_UNSIGNED_BYTE,
359                          gl_pixels.get());
360  }
361
362  gpu::Mailbox mailbox;
363  context3d->genMailboxCHROMIUM(mailbox.name);
364  context3d->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
365  context3d->bindTexture(GL_TEXTURE_2D, 0);
366  uint32 sync_point = context3d->insertSyncPoint();
367
368  return TextureMailbox(
369      mailbox,
370      base::Bind(&LayerTreePixelTest::ReleaseTextureMailbox,
371                 base::Unretained(this),
372                 base::Passed(&context3d),
373                 texture_id),
374      sync_point);
375}
376
377}  // namespace cc
378