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