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