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.
5 #include "cc/output/software_renderer.h"
7 #include "base/run_loop.h"
8 #include "cc/layers/quad_sink.h"
9 #include "cc/output/compositor_frame_metadata.h"
10 #include "cc/output/copy_output_request.h"
11 #include "cc/output/copy_output_result.h"
12 #include "cc/output/software_output_device.h"
13 #include "cc/quads/render_pass.h"
14 #include "cc/quads/render_pass_draw_quad.h"
15 #include "cc/quads/solid_color_draw_quad.h"
16 #include "cc/quads/tile_draw_quad.h"
17 #include "cc/test/animation_test_common.h"
18 #include "cc/test/fake_output_surface.h"
19 #include "cc/test/fake_output_surface_client.h"
20 #include "cc/test/geometry_test_utils.h"
21 #include "cc/test/render_pass_test_common.h"
22 #include "cc/test/render_pass_test_utils.h"
23 #include "cc/test/test_shared_bitmap_manager.h"
24 #include "testing/gmock/include/gmock/gmock.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "third_party/skia/include/core/SkCanvas.h"
31 class SoftwareRendererTest : public testing::Test, public RendererClient {
33 void InitializeRenderer(
34 scoped_ptr<SoftwareOutputDevice> software_output_device) {
35 output_surface_ = FakeOutputSurface::CreateSoftware(
36 software_output_device.Pass());
37 CHECK(output_surface_->BindToClient(&output_surface_client_));
39 shared_bitmap_manager_.reset(new TestSharedBitmapManager());
40 resource_provider_ = ResourceProvider::Create(
41 output_surface_.get(), shared_bitmap_manager_.get(), 0, false, 1,
43 renderer_ = SoftwareRenderer::Create(
44 this, &settings_, output_surface_.get(), resource_provider());
47 ResourceProvider* resource_provider() const {
48 return resource_provider_.get();
51 SoftwareRenderer* renderer() const { return renderer_.get(); }
53 // RendererClient implementation.
54 virtual void SetFullRootLayerDamage() OVERRIDE {}
55 virtual void RunOnDemandRasterTask(Task* on_demand_raster_task) OVERRIDE {}
57 scoped_ptr<SkBitmap> DrawAndCopyOutput(RenderPassList* list,
58 float device_scale_factor,
59 gfx::Rect device_viewport_rect) {
60 scoped_ptr<SkBitmap> bitmap_result;
63 list->back()->copy_requests.push_back(
64 CopyOutputRequest::CreateBitmapRequest(
65 base::Bind(&SoftwareRendererTest::SaveBitmapResult,
66 base::Unretained(&bitmap_result),
67 loop.QuitClosure())));
69 renderer()->DrawFrame(list,
75 return bitmap_result.Pass();
78 static void SaveBitmapResult(scoped_ptr<SkBitmap>* bitmap_result,
79 const base::Closure& quit_closure,
80 scoped_ptr<CopyOutputResult> result) {
81 DCHECK(result->HasBitmap());
82 *bitmap_result = result->TakeBitmap();
87 LayerTreeSettings settings_;
88 FakeOutputSurfaceClient output_surface_client_;
89 scoped_ptr<FakeOutputSurface> output_surface_;
90 scoped_ptr<SharedBitmapManager> shared_bitmap_manager_;
91 scoped_ptr<ResourceProvider> resource_provider_;
92 scoped_ptr<SoftwareRenderer> renderer_;
95 TEST_F(SoftwareRendererTest, SolidColorQuad) {
96 gfx::Size outer_size(100, 100);
97 gfx::Size inner_size(98, 98);
98 gfx::Rect outer_rect(outer_size);
99 gfx::Rect inner_rect(gfx::Point(1, 1), inner_size);
100 gfx::Rect visible_rect(gfx::Point(1, 2), gfx::Size(98, 97));
102 InitializeRenderer(make_scoped_ptr(new SoftwareOutputDevice));
104 RenderPass::Id root_render_pass_id = RenderPass::Id(1, 1);
105 scoped_ptr<TestRenderPass> root_render_pass = TestRenderPass::Create();
106 root_render_pass->SetNew(
107 root_render_pass_id, outer_rect, outer_rect, gfx::Transform());
108 SharedQuadState* shared_quad_state =
109 root_render_pass->CreateAndAppendSharedQuadState();
110 shared_quad_state->SetAll(gfx::Transform(),
116 SkXfermode::kSrcOver_Mode,
118 scoped_ptr<SolidColorDrawQuad> outer_quad = SolidColorDrawQuad::Create();
120 shared_quad_state, outer_rect, outer_rect, SK_ColorYELLOW, false);
121 scoped_ptr<SolidColorDrawQuad> inner_quad = SolidColorDrawQuad::Create();
123 shared_quad_state, inner_rect, inner_rect, SK_ColorCYAN, false);
124 inner_quad->visible_rect = visible_rect;
125 root_render_pass->AppendQuad(inner_quad.PassAs<DrawQuad>());
126 root_render_pass->AppendQuad(outer_quad.PassAs<DrawQuad>());
129 list.push_back(root_render_pass.PassAs<RenderPass>());
131 float device_scale_factor = 1.f;
132 gfx::Rect device_viewport_rect(outer_size);
133 scoped_ptr<SkBitmap> output =
134 DrawAndCopyOutput(&list, device_scale_factor, device_viewport_rect);
135 EXPECT_EQ(outer_rect.width(), output->info().fWidth);
136 EXPECT_EQ(outer_rect.width(), output->info().fHeight);
138 EXPECT_EQ(SK_ColorYELLOW, output->getColor(0, 0));
139 EXPECT_EQ(SK_ColorYELLOW,
140 output->getColor(outer_size.width() - 1, outer_size.height() - 1));
141 EXPECT_EQ(SK_ColorYELLOW, output->getColor(1, 1));
142 EXPECT_EQ(SK_ColorCYAN, output->getColor(1, 2));
143 EXPECT_EQ(SK_ColorCYAN,
144 output->getColor(inner_size.width() - 1, inner_size.height() - 1));
147 TEST_F(SoftwareRendererTest, TileQuad) {
148 gfx::Size outer_size(100, 100);
149 gfx::Size inner_size(98, 98);
150 gfx::Rect outer_rect(outer_size);
151 gfx::Rect inner_rect(gfx::Point(1, 1), inner_size);
152 InitializeRenderer(make_scoped_ptr(new SoftwareOutputDevice));
154 ResourceProvider::ResourceId resource_yellow =
155 resource_provider()->CreateResource(outer_size,
157 ResourceProvider::TextureUsageAny,
159 ResourceProvider::ResourceId resource_cyan =
160 resource_provider()->CreateResource(inner_size,
162 ResourceProvider::TextureUsageAny,
165 SkBitmap yellow_tile;
166 yellow_tile.setConfig(
167 SkBitmap::kARGB_8888_Config, outer_size.width(), outer_size.height());
168 yellow_tile.allocPixels();
169 yellow_tile.eraseColor(SK_ColorYELLOW);
173 SkBitmap::kARGB_8888_Config, inner_size.width(), inner_size.height());
174 cyan_tile.allocPixels();
175 cyan_tile.eraseColor(SK_ColorCYAN);
177 resource_provider()->SetPixels(
179 static_cast<uint8_t*>(yellow_tile.getPixels()),
180 gfx::Rect(outer_size),
181 gfx::Rect(outer_size),
183 resource_provider()->SetPixels(resource_cyan,
184 static_cast<uint8_t*>(cyan_tile.getPixels()),
185 gfx::Rect(inner_size),
186 gfx::Rect(inner_size),
189 gfx::Rect root_rect = outer_rect;
191 RenderPass::Id root_render_pass_id = RenderPass::Id(1, 1);
192 scoped_ptr<TestRenderPass> root_render_pass = TestRenderPass::Create();
193 root_render_pass->SetNew(
194 root_render_pass_id, root_rect, root_rect, gfx::Transform());
195 SharedQuadState* shared_quad_state =
196 root_render_pass->CreateAndAppendSharedQuadState();
197 shared_quad_state->SetAll(gfx::Transform(),
203 SkXfermode::kSrcOver_Mode,
205 scoped_ptr<TileDrawQuad> outer_quad = TileDrawQuad::Create();
206 outer_quad->SetNew(shared_quad_state,
211 gfx::RectF(outer_size),
214 scoped_ptr<TileDrawQuad> inner_quad = TileDrawQuad::Create();
215 inner_quad->SetNew(shared_quad_state,
220 gfx::RectF(inner_size),
223 root_render_pass->AppendQuad(inner_quad.PassAs<DrawQuad>());
224 root_render_pass->AppendQuad(outer_quad.PassAs<DrawQuad>());
227 list.push_back(root_render_pass.PassAs<RenderPass>());
229 float device_scale_factor = 1.f;
230 gfx::Rect device_viewport_rect(outer_size);
231 scoped_ptr<SkBitmap> output =
232 DrawAndCopyOutput(&list, device_scale_factor, device_viewport_rect);
233 EXPECT_EQ(outer_rect.width(), output->info().fWidth);
234 EXPECT_EQ(outer_rect.width(), output->info().fHeight);
236 EXPECT_EQ(SK_ColorYELLOW, output->getColor(0, 0));
237 EXPECT_EQ(SK_ColorYELLOW,
238 output->getColor(outer_size.width() - 1, outer_size.height() - 1));
239 EXPECT_EQ(SK_ColorCYAN, output->getColor(1, 1));
240 EXPECT_EQ(SK_ColorCYAN,
241 output->getColor(inner_size.width() - 1, inner_size.height() - 1));
244 TEST_F(SoftwareRendererTest, TileQuadVisibleRect) {
245 gfx::Size tile_size(100, 100);
246 gfx::Rect tile_rect(tile_size);
247 gfx::Rect visible_rect = tile_rect;
248 visible_rect.Inset(1, 2, 3, 4);
249 InitializeRenderer(make_scoped_ptr(new SoftwareOutputDevice));
251 ResourceProvider::ResourceId resource_cyan =
252 resource_provider()->CreateResource(tile_size,
254 ResourceProvider::TextureUsageAny,
257 SkBitmap cyan_tile; // The lowest five rows are yellow.
259 SkBitmap::kARGB_8888_Config, tile_size.width(), tile_size.height());
260 cyan_tile.allocPixels();
261 cyan_tile.eraseColor(SK_ColorCYAN);
264 0, visible_rect.bottom() - 1, tile_rect.width(), tile_rect.bottom()),
267 resource_provider()->SetPixels(resource_cyan,
268 static_cast<uint8_t*>(cyan_tile.getPixels()),
269 gfx::Rect(tile_size),
270 gfx::Rect(tile_size),
273 gfx::Rect root_rect(tile_size);
275 RenderPass::Id root_render_pass_id = RenderPass::Id(1, 1);
276 scoped_ptr<TestRenderPass> root_render_pass = TestRenderPass::Create();
277 root_render_pass->SetNew(
278 root_render_pass_id, root_rect, root_rect, gfx::Transform());
279 SharedQuadState* shared_quad_state =
280 root_render_pass->CreateAndAppendSharedQuadState();
281 shared_quad_state->SetAll(gfx::Transform(),
287 SkXfermode::kSrcOver_Mode,
289 scoped_ptr<TileDrawQuad> quad = TileDrawQuad::Create();
290 quad->SetNew(shared_quad_state,
295 gfx::RectF(tile_size),
298 quad->visible_rect = visible_rect;
299 root_render_pass->AppendQuad(quad.PassAs<DrawQuad>());
302 list.push_back(root_render_pass.PassAs<RenderPass>());
304 float device_scale_factor = 1.f;
305 gfx::Rect device_viewport_rect(tile_size);
306 scoped_ptr<SkBitmap> output =
307 DrawAndCopyOutput(&list, device_scale_factor, device_viewport_rect);
308 EXPECT_EQ(tile_rect.width(), output->info().fWidth);
309 EXPECT_EQ(tile_rect.width(), output->info().fHeight);
311 // Check portion of tile not in visible rect isn't drawn.
312 const unsigned int kTransparent = SK_ColorTRANSPARENT;
313 EXPECT_EQ(kTransparent, output->getColor(0, 0));
314 EXPECT_EQ(kTransparent,
315 output->getColor(tile_rect.width() - 1, tile_rect.height() - 1));
316 EXPECT_EQ(kTransparent,
317 output->getColor(visible_rect.x() - 1, visible_rect.y() - 1));
318 EXPECT_EQ(kTransparent,
319 output->getColor(visible_rect.right(), visible_rect.bottom()));
320 // Ensure visible part is drawn correctly.
321 EXPECT_EQ(SK_ColorCYAN, output->getColor(visible_rect.x(), visible_rect.y()));
324 output->getColor(visible_rect.right() - 2, visible_rect.bottom() - 2));
325 // Ensure last visible line is correct.
328 output->getColor(visible_rect.right() - 1, visible_rect.bottom() - 1));
331 TEST_F(SoftwareRendererTest, ShouldClearRootRenderPass) {
332 float device_scale_factor = 1.f;
333 gfx::Rect device_viewport_rect(0, 0, 100, 100);
335 settings_.should_clear_root_render_pass = false;
336 InitializeRenderer(make_scoped_ptr(new SoftwareOutputDevice));
340 // Draw a fullscreen green quad in a first frame.
341 RenderPass::Id root_clear_pass_id(1, 0);
342 TestRenderPass* root_clear_pass = AddRenderPass(
343 &list, root_clear_pass_id, device_viewport_rect, gfx::Transform());
344 AddQuad(root_clear_pass, device_viewport_rect, SK_ColorGREEN);
346 renderer()->DecideRenderPassAllocationsForFrame(list);
348 scoped_ptr<SkBitmap> output =
349 DrawAndCopyOutput(&list, device_scale_factor, device_viewport_rect);
350 EXPECT_EQ(device_viewport_rect.width(), output->info().fWidth);
351 EXPECT_EQ(device_viewport_rect.width(), output->info().fHeight);
353 EXPECT_EQ(SK_ColorGREEN, output->getColor(0, 0));
354 EXPECT_EQ(SK_ColorGREEN,
355 output->getColor(device_viewport_rect.width() - 1,
356 device_viewport_rect.height() - 1));
360 // Draw a smaller magenta rect without filling the viewport in a separate
362 gfx::Rect smaller_rect(20, 20, 60, 60);
364 RenderPass::Id root_smaller_pass_id(2, 0);
365 TestRenderPass* root_smaller_pass = AddRenderPass(
366 &list, root_smaller_pass_id, device_viewport_rect, gfx::Transform());
367 AddQuad(root_smaller_pass, smaller_rect, SK_ColorMAGENTA);
369 renderer()->DecideRenderPassAllocationsForFrame(list);
371 output = DrawAndCopyOutput(&list, device_scale_factor, device_viewport_rect);
372 EXPECT_EQ(device_viewport_rect.width(), output->info().fWidth);
373 EXPECT_EQ(device_viewport_rect.width(), output->info().fHeight);
375 // If we didn't clear, the borders should still be green.
376 EXPECT_EQ(SK_ColorGREEN, output->getColor(0, 0));
377 EXPECT_EQ(SK_ColorGREEN,
378 output->getColor(device_viewport_rect.width() - 1,
379 device_viewport_rect.height() - 1));
381 EXPECT_EQ(SK_ColorMAGENTA,
382 output->getColor(smaller_rect.x(), smaller_rect.y()));
385 output->getColor(smaller_rect.right() - 1, smaller_rect.bottom() - 1));
388 TEST_F(SoftwareRendererTest, RenderPassVisibleRect) {
389 float device_scale_factor = 1.f;
390 gfx::Rect device_viewport_rect(0, 0, 100, 100);
391 InitializeRenderer(make_scoped_ptr(new SoftwareOutputDevice));
395 // Pass drawn as inner quad is magenta.
396 gfx::Rect smaller_rect(20, 20, 60, 60);
397 RenderPass::Id smaller_pass_id(2, 1);
398 TestRenderPass* smaller_pass =
399 AddRenderPass(&list, smaller_pass_id, smaller_rect, gfx::Transform());
400 AddQuad(smaller_pass, smaller_rect, SK_ColorMAGENTA);
402 // Root pass is green.
403 RenderPass::Id root_clear_pass_id(1, 0);
404 TestRenderPass* root_clear_pass = AddRenderPass(
405 &list, root_clear_pass_id, device_viewport_rect, gfx::Transform());
406 AddRenderPassQuad(root_clear_pass, smaller_pass);
407 AddQuad(root_clear_pass, device_viewport_rect, SK_ColorGREEN);
409 // Interior pass quad has smaller visible rect.
410 gfx::Rect interior_visible_rect(30, 30, 40, 40);
411 root_clear_pass->quad_list[0]->visible_rect = interior_visible_rect;
413 renderer()->DecideRenderPassAllocationsForFrame(list);
415 scoped_ptr<SkBitmap> output =
416 DrawAndCopyOutput(&list, device_scale_factor, device_viewport_rect);
417 EXPECT_EQ(device_viewport_rect.width(), output->info().fWidth);
418 EXPECT_EQ(device_viewport_rect.width(), output->info().fHeight);
420 EXPECT_EQ(SK_ColorGREEN, output->getColor(0, 0));
421 EXPECT_EQ(SK_ColorGREEN,
422 output->getColor(device_viewport_rect.width() - 1,
423 device_viewport_rect.height() - 1));
425 // Part outside visible rect should remain green.
426 EXPECT_EQ(SK_ColorGREEN,
427 output->getColor(smaller_rect.x(), smaller_rect.y()));
430 output->getColor(smaller_rect.right() - 1, smaller_rect.bottom() - 1));
434 output->getColor(interior_visible_rect.x(), interior_visible_rect.y()));
435 EXPECT_EQ(SK_ColorMAGENTA,
436 output->getColor(interior_visible_rect.right() - 1,
437 interior_visible_rect.bottom() - 1));