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.
5 #include "cc/test/layer_tree_pixel_test.h"
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"
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) {}
33 LayerTreePixelTest::~LayerTreePixelTest() {}
35 scoped_ptr<OutputSurface> LayerTreePixelTest::CreateOutputSurface(
37 gfx::Vector2d viewport_offset(20, 10);
38 gfx::Size surface_expansion_size(40, 60);
39 scoped_ptr<PixelTestOutputSurface> output_surface;
42 case SOFTWARE_WITH_DEFAULT:
43 case SOFTWARE_WITH_BITMAP: {
44 scoped_ptr<PixelTestSoftwareOutputDevice> software_output_device(
45 new PixelTestSoftwareOutputDevice);
46 software_output_device->set_surface_expansion_size(
47 surface_expansion_size);
48 output_surface = make_scoped_ptr(
49 new PixelTestOutputSurface(
50 software_output_device.PassAs<SoftwareOutputDevice>()));
55 case GL_WITH_BITMAP: {
56 CHECK(gfx::InitializeGLBindings(gfx::kGLImplementationOSMesaGL));
58 using webkit::gpu::ContextProviderInProcess;
59 output_surface = make_scoped_ptr(new PixelTestOutputSurface(
60 ContextProviderInProcess::CreateOffscreen()));
65 output_surface->set_viewport_offset(viewport_offset);
66 output_surface->set_surface_expansion_size(surface_expansion_size);
67 return output_surface.PassAs<OutputSurface>();
70 scoped_refptr<cc::ContextProvider>
71 LayerTreePixelTest::OffscreenContextProvider() {
72 scoped_refptr<webkit::gpu::ContextProviderInProcess> provider =
73 webkit::gpu::ContextProviderInProcess::CreateOffscreen();
74 CHECK(provider.get());
78 scoped_ptr<CopyOutputRequest> LayerTreePixelTest::CreateCopyOutputRequest() {
79 return CopyOutputRequest::CreateBitmapRequest(
80 base::Bind(&LayerTreePixelTest::ReadbackResult, base::Unretained(this)));
83 void LayerTreePixelTest::ReadbackResult(scoped_ptr<CopyOutputResult> result) {
84 ASSERT_TRUE(result->HasBitmap());
85 result_bitmap_ = result->TakeBitmap().Pass();
89 void LayerTreePixelTest::BeginTest() {
90 Layer* target = readback_target_ ? readback_target_
91 : layer_tree_host()->root_layer();
92 target->RequestCopyOfOutput(CreateCopyOutputRequest().Pass());
93 PostSetNeedsCommitToMainThread();
96 void LayerTreePixelTest::AfterTest() {
97 base::FilePath test_data_dir;
98 EXPECT_TRUE(PathService::Get(cc::DIR_TEST_DATA, &test_data_dir));
99 base::FilePath ref_file_path = test_data_dir.Append(ref_file_);
101 CommandLine* cmd = CommandLine::ForCurrentProcess();
102 if (cmd->HasSwitch(switches::kCCRebaselinePixeltests))
103 EXPECT_TRUE(WritePNGFile(*result_bitmap_, ref_file_path, true));
105 EXPECT_TRUE(MatchesPNGFile(*result_bitmap_,
107 *pixel_comparator_));
110 scoped_refptr<SolidColorLayer> LayerTreePixelTest::CreateSolidColorLayer(
111 gfx::Rect rect, SkColor color) {
112 scoped_refptr<SolidColorLayer> layer = SolidColorLayer::Create();
113 layer->SetIsDrawable(true);
114 layer->SetAnchorPoint(gfx::PointF());
115 layer->SetBounds(rect.size());
116 layer->SetPosition(rect.origin());
117 layer->SetBackgroundColor(color);
121 void LayerTreePixelTest::EndTest() {
122 // Drop TextureMailboxes on the main thread so that they can be cleaned up and
123 // the pending callbacks will fire.
124 for (size_t i = 0; i < texture_layers_.size(); ++i) {
125 texture_layers_[i]->SetTextureMailbox(TextureMailbox(),
126 scoped_ptr<SingleReleaseCallback>());
132 void LayerTreePixelTest::TryEndTest() {
135 if (pending_texture_mailbox_callbacks_)
137 LayerTreeTest::EndTest();
140 scoped_refptr<SolidColorLayer> LayerTreePixelTest::
141 CreateSolidColorLayerWithBorder(
142 gfx::Rect rect, SkColor color, int border_width, SkColor border_color) {
143 scoped_refptr<SolidColorLayer> layer = CreateSolidColorLayer(rect, color);
144 scoped_refptr<SolidColorLayer> border_top = CreateSolidColorLayer(
145 gfx::Rect(0, 0, rect.width(), border_width), border_color);
146 scoped_refptr<SolidColorLayer> border_left = CreateSolidColorLayer(
150 rect.height() - border_width * 2),
152 scoped_refptr<SolidColorLayer> border_right =
153 CreateSolidColorLayer(gfx::Rect(rect.width() - border_width,
156 rect.height() - border_width * 2),
158 scoped_refptr<SolidColorLayer> border_bottom = CreateSolidColorLayer(
159 gfx::Rect(0, rect.height() - border_width, rect.width(), border_width),
161 layer->AddChild(border_top);
162 layer->AddChild(border_left);
163 layer->AddChild(border_right);
164 layer->AddChild(border_bottom);
168 scoped_refptr<TextureLayer> LayerTreePixelTest::CreateTextureLayer(
169 gfx::Rect rect, const SkBitmap& bitmap) {
170 scoped_refptr<TextureLayer> layer = TextureLayer::CreateForMailbox(NULL);
171 layer->SetIsDrawable(true);
172 layer->SetAnchorPoint(gfx::PointF());
173 layer->SetBounds(rect.size());
174 layer->SetPosition(rect.origin());
176 TextureMailbox texture_mailbox;
177 scoped_ptr<SingleReleaseCallback> release_callback;
178 CopyBitmapToTextureMailboxAsTexture(
179 bitmap, &texture_mailbox, &release_callback);
180 layer->SetTextureMailbox(texture_mailbox, release_callback.Pass());
182 texture_layers_.push_back(layer);
183 pending_texture_mailbox_callbacks_++;
187 void LayerTreePixelTest::RunPixelTest(
188 PixelTestType test_type,
189 scoped_refptr<Layer> content_root,
190 base::FilePath file_name) {
191 test_type_ = test_type;
192 content_root_ = content_root;
193 readback_target_ = NULL;
194 ref_file_ = file_name;
195 RunTest(true, false, impl_side_painting_);
198 void LayerTreePixelTest::RunPixelTestWithReadbackTarget(
199 PixelTestType test_type,
200 scoped_refptr<Layer> content_root,
202 base::FilePath file_name) {
203 test_type_ = test_type;
204 content_root_ = content_root;
205 readback_target_ = target;
206 ref_file_ = file_name;
207 RunTest(true, false, impl_side_painting_);
210 void LayerTreePixelTest::SetupTree() {
211 scoped_refptr<Layer> root = Layer::Create();
212 root->SetBounds(content_root_->bounds());
213 root->AddChild(content_root_);
214 layer_tree_host()->SetRootLayer(root);
215 LayerTreeTest::SetupTree();
218 scoped_ptr<SkBitmap> LayerTreePixelTest::CopyTextureMailboxToBitmap(
220 const TextureMailbox& texture_mailbox) {
221 DCHECK(texture_mailbox.IsTexture());
222 if (!texture_mailbox.IsTexture())
223 return scoped_ptr<SkBitmap>();
225 using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl;
226 scoped_ptr<WebKit::WebGraphicsContext3D> context3d(
227 WebGraphicsContext3DInProcessCommandBufferImpl::CreateOffscreenContext(
228 WebKit::WebGraphicsContext3D::Attributes()));
230 EXPECT_TRUE(context3d->makeContextCurrent());
232 if (texture_mailbox.sync_point())
233 context3d->waitSyncPoint(texture_mailbox.sync_point());
235 unsigned texture_id = context3d->createTexture();
236 context3d->bindTexture(GL_TEXTURE_2D, texture_id);
237 context3d->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
238 context3d->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
239 context3d->texParameteri(
240 GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
241 context3d->texParameteri(
242 GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
243 context3d->consumeTextureCHROMIUM(texture_mailbox.target(),
244 texture_mailbox.data());
245 context3d->bindTexture(GL_TEXTURE_2D, 0);
247 unsigned fbo = context3d->createFramebuffer();
248 context3d->bindFramebuffer(GL_FRAMEBUFFER, fbo);
249 context3d->framebufferTexture2D(GL_FRAMEBUFFER,
250 GL_COLOR_ATTACHMENT0,
254 EXPECT_EQ(static_cast<unsigned>(GL_FRAMEBUFFER_COMPLETE),
255 context3d->checkFramebufferStatus(GL_FRAMEBUFFER));
257 scoped_ptr<uint8[]> pixels(new uint8[size.GetArea() * 4]);
258 context3d->readPixels(0,
266 context3d->deleteFramebuffer(fbo);
267 context3d->deleteTexture(texture_id);
269 scoped_ptr<SkBitmap> bitmap(new SkBitmap);
270 bitmap->setConfig(SkBitmap::kARGB_8888_Config,
273 bitmap->allocPixels();
275 scoped_ptr<SkAutoLockPixels> lock(new SkAutoLockPixels(*bitmap));
276 uint8* out_pixels = static_cast<uint8*>(bitmap->getPixels());
278 size_t row_bytes = size.width() * 4;
279 size_t total_bytes = size.height() * row_bytes;
280 for (size_t dest_y = 0; dest_y < total_bytes; dest_y += row_bytes) {
282 size_t src_y = total_bytes - dest_y - row_bytes;
283 // Swizzle OpenGL -> Skia byte order.
284 for (size_t x = 0; x < row_bytes; x += 4) {
285 out_pixels[dest_y + x + SK_R32_SHIFT/8] = pixels.get()[src_y + x + 0];
286 out_pixels[dest_y + x + SK_G32_SHIFT/8] = pixels.get()[src_y + x + 1];
287 out_pixels[dest_y + x + SK_B32_SHIFT/8] = pixels.get()[src_y + x + 2];
288 out_pixels[dest_y + x + SK_A32_SHIFT/8] = pixels.get()[src_y + x + 3];
292 return bitmap.Pass();
295 void LayerTreePixelTest::ReleaseTextureMailbox(
296 scoped_ptr<WebKit::WebGraphicsContext3D> context3d,
299 bool lost_resource) {
301 context3d->waitSyncPoint(sync_point);
302 context3d->deleteTexture(texture);
303 pending_texture_mailbox_callbacks_--;
307 void LayerTreePixelTest::CopyBitmapToTextureMailboxAsTexture(
308 const SkBitmap& bitmap,
309 TextureMailbox* texture_mailbox,
310 scoped_ptr<SingleReleaseCallback>* release_callback) {
311 DCHECK_GT(bitmap.width(), 0);
312 DCHECK_GT(bitmap.height(), 0);
314 CHECK(gfx::InitializeGLBindings(gfx::kGLImplementationOSMesaGL));
316 using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl;
317 scoped_ptr<WebKit::WebGraphicsContext3D> context3d(
318 WebGraphicsContext3DInProcessCommandBufferImpl::CreateOffscreenContext(
319 WebKit::WebGraphicsContext3D::Attributes()));
321 EXPECT_TRUE(context3d->makeContextCurrent());
323 unsigned texture_id = context3d->createTexture();
324 context3d->bindTexture(GL_TEXTURE_2D, texture_id);
325 context3d->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
326 context3d->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
327 context3d->texParameteri(
328 GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
329 context3d->texParameteri(
330 GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
332 DCHECK_EQ(SkBitmap::kARGB_8888_Config, bitmap.getConfig());
335 SkAutoLockPixels lock(bitmap);
337 size_t row_bytes = bitmap.width() * 4;
338 size_t total_bytes = bitmap.height() * row_bytes;
340 scoped_ptr<uint8[]> gl_pixels(new uint8[total_bytes]);
341 uint8* bitmap_pixels = static_cast<uint8*>(bitmap.getPixels());
343 for (size_t y = 0; y < total_bytes; y += row_bytes) {
345 size_t src_y = total_bytes - y - row_bytes;
346 // Swizzle Skia -> OpenGL byte order.
347 for (size_t x = 0; x < row_bytes; x += 4) {
348 gl_pixels.get()[y + x + 0] = bitmap_pixels[src_y + x + SK_R32_SHIFT/8];
349 gl_pixels.get()[y + x + 1] = bitmap_pixels[src_y + x + SK_G32_SHIFT/8];
350 gl_pixels.get()[y + x + 2] = bitmap_pixels[src_y + x + SK_B32_SHIFT/8];
351 gl_pixels.get()[y + x + 3] = bitmap_pixels[src_y + x + SK_A32_SHIFT/8];
355 context3d->texImage2D(GL_TEXTURE_2D,
366 gpu::Mailbox mailbox;
367 context3d->genMailboxCHROMIUM(mailbox.name);
368 context3d->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
369 context3d->bindTexture(GL_TEXTURE_2D, 0);
370 uint32 sync_point = context3d->insertSyncPoint();
372 *texture_mailbox = TextureMailbox(mailbox, sync_point);
373 *release_callback = SingleReleaseCallback::Create(
374 base::Bind(&LayerTreePixelTest::ReleaseTextureMailbox,
375 base::Unretained(this),
376 base::Passed(&context3d),